2 * Copyright (c) 2012 - 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
28 #include <Ecore_IMF.h>
31 #include "websocket.h"
36 #include <libwebsockets.h>
39 #define EFL_BETA_API_SUPPORT
40 #include <Ecore_Wl2.h>
43 #define WEBSOCKET_PORT 7681
45 #define RECVED_MESSAGE "recved"
46 #define MESSAGE_LEFT "left"
48 pthread_t g_ws_server_thread = (pthread_t)NULL;
49 pthread_mutex_t g_ws_server_mutex = PTHREAD_MUTEX_INITIALIZER;
51 pthread_cond_t g_ws_query_condition = PTHREAD_COND_INITIALIZER;
52 pthread_mutex_t g_ws_query_mutex = PTHREAD_MUTEX_INITIALIZER;
54 bool g_ws_server_exit = false;
55 struct lws_context *g_ws_server_context = NULL;
57 CWebHelperAgentWebSocket* CWebHelperAgentWebSocket::m_current_instance = NULL;
71 struct per_session_data__http {
75 static int callback_http(struct lws *wsi,
76 enum lws_callback_reasons reason,
77 void *user, void *in, size_t len)
82 struct per_session_data__keyboard {
89 static int callback_keyboard(struct lws *wsi,
90 enum lws_callback_reasons reason,
91 void *user, void *in, size_t len);
93 static struct lws_protocols protocols[] = {
97 sizeof(struct per_session_data__http),
103 sizeof(struct per_session_data__keyboard),
110 static int callback_client(struct lws *wsi,
111 enum lws_callback_reasons reason,
112 void *user, void *in, size_t len)
115 case LWS_CALLBACK_CLIENT_ESTABLISHED:
116 LOGD("[ClientTest] Connection established");
119 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
120 LOGD("[ClientTest] Connection error");
123 case LWS_CALLBACK_CLOSED:
124 LOGD("[ClientTest] Connection closed");
134 int test_client_connection(void)
136 struct lws_context *context = NULL;
137 struct lws_context_creation_info context_info;
138 struct lws_client_connect_info connect_info;
139 struct lws *wsi = NULL;
141 memset(&context_info, 0, sizeof context_info);
142 memset(&connect_info, 0, sizeof(connect_info));
144 const int protocols_num = sizeof(protocols) / sizeof(lws_protocols);
145 static struct lws_protocols client_protocols[protocols_num];
147 memcpy(&client_protocols, protocols, sizeof(protocols));
148 for (int loop = 0; loop < protocols_num - 1; loop++) {
149 client_protocols[loop].callback = callback_client;
152 context_info.port = CONTEXT_PORT_NO_LISTEN;
153 context_info.protocols = protocols;
154 context_info.gid = -1;
155 context_info.uid = -1;
157 context = lws_create_context(&context_info);
158 LOGD("[ClientTest] create_context : %p", context);
159 if (context == NULL) {
163 connect_info.address = "localhost";
164 connect_info.port = WEBSOCKET_PORT;
165 connect_info.path = "/";
166 connect_info.context = context;
167 connect_info.ssl_connection = 0;
168 connect_info.host = connect_info.address;
169 connect_info.origin = connect_info.address;
170 connect_info.ietf_version_or_minus_one = -1;
171 connect_info.protocol = "keyboard-protocol";
173 wsi = lws_client_connect_via_info(&connect_info);
174 LOGD("[ClientTest] wsi created : %p", wsi);
177 lws_service(context, 50);
180 lws_context_destroy(context);
185 static Ecore_Timer *g_flush_server_recv_buffer_timer = NULL;
186 static std::string server_recv_buffer;
187 static Eina_Bool flush_server_recv_buffer_func(void *user)
189 LOGD("flushing recv buffer");
190 CWebHelperAgentWebSocket *agent = CWebHelperAgentWebSocket::get_current_instance();
191 struct per_session_data__keyboard *pss = (struct per_session_data__keyboard *)user;
193 if (g_flush_server_recv_buffer_timer)
194 ecore_timer_del(g_flush_server_recv_buffer_timer);
195 g_flush_server_recv_buffer_timer = NULL;
197 if (!agent || !pss) return ECORE_CALLBACK_CANCEL;
199 pthread_mutex_lock(&g_ws_server_mutex);
200 ISE_MESSAGE message = CISEMessageSerializer::deserialize(server_recv_buffer);
201 server_recv_buffer.clear();
202 pthread_mutex_unlock(&g_ws_server_mutex);
204 if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_LOGIN]) == 0) {
206 if (message.values.at(0).compare(CMagicKeyManager::get_magic_key()) == 0) {
207 LOGD("LOGIN successful, validating client");
210 LOGD("LOGIN failed, invalidating client");
216 if (agent->initalized()) {
217 pss->need_init = true;
218 ecore_pipe_write(agent->get_message_pipe(), MESSAGE_LEFT, strlen(MESSAGE_LEFT));
222 /* Ignore valid check since the magic key is not used anymore */
223 if (!pss->valid) pss->valid = true;
226 pthread_mutex_lock(&g_ws_server_mutex);
227 std::queue<ISE_MESSAGE>& messages = agent->get_recv_message_queue();
228 messages.push(message);
229 pthread_mutex_unlock(&g_ws_server_mutex);
231 ecore_pipe_write(agent->get_message_pipe(), RECVED_MESSAGE, strlen(RECVED_MESSAGE));
233 /* If we received reply message, let's send signal to wake up our main thread */
234 if (message.type.compare(ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY]) == 0) {
235 pthread_mutex_lock(&g_ws_query_mutex);
236 pthread_cond_signal(&g_ws_query_condition);
237 pthread_mutex_unlock(&g_ws_query_mutex);
240 LOGD("Ignoring data received since client is not valid %d", pss->session_id);
243 return ECORE_CALLBACK_CANCEL;
246 static int callback_keyboard(struct lws *wsi,
247 enum lws_callback_reasons reason,
248 void *user, void *in, size_t len)
250 static int last_session_id = 0;
251 const int bufsize = 512;
253 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + bufsize +
254 LWS_SEND_BUFFER_POST_PADDING];
255 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
256 struct per_session_data__keyboard *pss = (struct per_session_data__keyboard *)user;
257 CWebHelperAgentWebSocket *agent = CWebHelperAgentWebSocket::get_current_instance();
260 case LWS_CALLBACK_ESTABLISHED:
261 pss->session_id = ++last_session_id;
262 LOGD("LWS_CALLBACK_ESTABLISHED : %p %d", g_ws_server_context, pss->session_id);
264 pss->need_init = false;
265 pss->initialized = false;
266 if (g_ws_server_context) {
267 ecore_pipe_write(agent->get_message_pipe(), MESSAGE_LEFT, strlen(MESSAGE_LEFT));
271 case LWS_CALLBACK_CLOSED:
272 LOGD("LWS_CALLBACK_CLOSED : %d", pss->session_id);
275 case LWS_CALLBACK_SERVER_WRITEABLE:
277 /* Ignore valid check since the magic key is not used anymore */
278 if (!pss->valid) pss->valid = true;
280 /* We allow data tranmission only if this client is guaranteed to be valid */
282 pthread_mutex_lock(&g_ws_server_mutex);
283 std::queue<ISE_MESSAGE>& messages = agent->get_send_message_queue();
285 if (pss->need_init && !pss->initialized) {
287 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
288 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_INIT];
289 std::string str = CISEMessageSerializer::serialize(message);
290 SECURE_LOGD("SEND_WEBSOCKET_MESSAGE : %d %s", pss->session_id, str.c_str());
291 n = snprintf((char *)p, bufsize, "%s", str.c_str());
292 /* too small for partial */
293 n = lws_write(wsi, p, n, LWS_WRITE_TEXT);
294 pss->need_init = false;
295 pss->initialized = true;
297 /* One write allowed per one writable callback */
298 if (messages.size() > 0) {
299 ISE_MESSAGE &message = messages.front();
301 if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_INIT]) == 0) {
302 if (pss->initialized) {
305 pss->initialized = true;
309 std::string str = CISEMessageSerializer::serialize(message);
310 SECURE_LOGD("SEND_WEBSOCKET_MESSAGE : %d %s", pss->session_id, str.c_str());
311 n = snprintf((char *)p, bufsize, "%s", str.c_str());
312 /* too small for partial */
313 n = lws_write(wsi, p, n, LWS_WRITE_TEXT);
318 if (messages.size() > 0) {
319 ecore_pipe_write(agent->get_message_pipe(), MESSAGE_LEFT, strlen(MESSAGE_LEFT));
321 pthread_mutex_unlock(&g_ws_server_mutex);
324 LOGE("ERROR %d writing to di socket %d", n, pss->session_id);
327 if (messages.size() > 0) {
328 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
331 LOGD("Rejecting data transmission since client is not valid : %d", pss->session_id);
336 case LWS_CALLBACK_RECEIVE:
338 if (g_flush_server_recv_buffer_timer) {
339 ecore_thread_main_loop_begin();
340 ecore_timer_del(g_flush_server_recv_buffer_timer);
341 g_flush_server_recv_buffer_timer = NULL;
342 ecore_thread_main_loop_end();
345 std::string str = (const char *)in;
346 if (CISEMessageSerializer::valid(str)) {
347 LOGD("A valid new message received, flush previous buffer");
348 flush_server_recv_buffer_func((void*)pss);
351 pthread_mutex_lock(&g_ws_server_mutex);
352 server_recv_buffer += str;
353 SECURE_LOGD("RECEIVE callback : [%s], [%s]", str.c_str(), server_recv_buffer.c_str());
354 pthread_mutex_unlock(&g_ws_server_mutex);
356 ecore_thread_main_loop_begin();
357 g_flush_server_recv_buffer_timer = ecore_timer_add(0.05, flush_server_recv_buffer_func, (void*)pss);
358 SECURE_LOGD("flush timer registered : %p", g_flush_server_recv_buffer_timer);
359 ecore_thread_main_loop_end();
370 void *process_ws_server(void *data)
372 while (!force_exit && !g_ws_server_exit) {
374 gettimeofday(&tv, NULL);
376 if (g_ws_server_context) {
377 lws_service(g_ws_server_context, 50);
379 LOGD("WARNING : g_ws_server_context is NULL");
382 LOGD("process_ws_server exits now");
386 void log_func(int level, const char *line)
389 LOGD("LEVEL : %d , %s", level, line);
393 CWebHelperAgentWebSocket::CWebHelperAgentWebSocket()
395 if (m_current_instance != NULL) {
396 LOGD("WARNING : m_current_instance is NOT NULL");
398 m_current_instance = this;
399 m_message_pipe = NULL;
400 m_initialized = false;
403 CWebHelperAgentWebSocket::~CWebHelperAgentWebSocket()
405 if (m_current_instance == this) {
406 m_current_instance = NULL;
409 if (m_message_pipe) {
410 ecore_pipe_del(m_message_pipe);
411 m_message_pipe = NULL;
415 static void message_pipe_handler(void *data, void *buffer, unsigned int nbyte)
418 if (strncmp((const char*)buffer, RECVED_MESSAGE, strlen(RECVED_MESSAGE)) == 0) {
419 CWebHelperAgentWebSocket *agent = CWebHelperAgentWebSocket::get_current_instance();
421 agent->process_recved_messages();
424 if (g_ws_server_context) {
425 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
427 LOGD("WARNING : g_ws_server_context is NULL");
433 bool CWebHelperAgentWebSocket::init()
437 struct lws_context_creation_info info;
439 memset(&info, 0, sizeof info);
440 info.port = WEBSOCKET_PORT;
442 int log_level = LLL_ERR | LLL_WARN | LLL_DEBUG;
443 lws_set_log_level(log_level, log_func);
446 info.protocols = protocols;
447 info.extensions = NULL;
448 info.ssl_cert_filepath = NULL;
449 info.ssl_private_key_filepath = NULL;
456 /* The WebSocket server is running on a separate thread, and let the thread send a message
457 through this pipe to guarantee thread safety */
458 m_message_pipe = ecore_pipe_add(message_pipe_handler, NULL);
460 /* Let's retry creating server context for a certain number of times */
461 const int max_retry_num = 30;
465 g_ws_server_context = lws_create_context(&info);
466 LOGD("libwebsocket context : %p", g_ws_server_context);
468 } while (g_ws_server_context == NULL && retry_num++ < max_retry_num);
470 pthread_mutex_init(&g_ws_server_mutex, NULL);
472 pthread_mutex_init(&g_ws_query_mutex, NULL);
473 pthread_cond_init(&g_ws_query_condition, NULL);
475 if (g_ws_server_context) {
476 if (pthread_create(&g_ws_server_thread, NULL, &process_ws_server, NULL) != 0) {
477 g_ws_server_thread = (pthread_t)NULL;
481 LOGE("Failed creating server context : %p", g_ws_server_context);
487 m_initialized = true;
489 test_client_connection();
494 bool CWebHelperAgentWebSocket::exit()
496 if (g_flush_server_recv_buffer_timer)
497 ecore_timer_del(g_flush_server_recv_buffer_timer);
498 g_flush_server_recv_buffer_timer = NULL;
502 g_ws_server_exit = true;
503 if (g_ws_server_context) {
504 lws_cancel_service(g_ws_server_context);
507 if (m_message_pipe) {
508 ecore_pipe_del(m_message_pipe);
509 m_message_pipe = NULL;
512 if (g_ws_server_thread) {
513 pthread_join(g_ws_server_thread, NULL);
516 pthread_cond_destroy(&g_ws_query_condition);
517 pthread_mutex_destroy(&g_ws_query_mutex);
519 pthread_mutex_destroy(&g_ws_server_mutex);
521 if (g_ws_server_context) {
522 lws_context_destroy(g_ws_server_context);
523 g_ws_server_context = NULL;
531 void CWebHelperAgentWebSocket::signal(int sig)
537 std::string to_string(T i)
539 std::stringstream ss;
547 void CWebHelperAgentWebSocket::on_init()
550 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
551 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_INIT];
553 pthread_mutex_lock(&g_ws_server_mutex);
554 m_send_message_queue.push(message);
555 pthread_mutex_unlock(&g_ws_server_mutex);
557 if (g_ws_server_context) {
558 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
560 LOGD("WARNING : g_ws_server_context is NULL");
564 void CWebHelperAgentWebSocket::on_exit()
567 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
568 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_EXIT];
570 pthread_mutex_lock(&g_ws_server_mutex);
571 m_send_message_queue.push(message);
572 pthread_mutex_unlock(&g_ws_server_mutex);
574 if (g_ws_server_context) {
575 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
577 LOGD("WARNING : g_ws_server_context is NULL");
581 void CWebHelperAgentWebSocket::on_focus_in(int ic)
584 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
585 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_FOCUS_IN];
586 message.values.push_back(to_string(ic));
588 pthread_mutex_lock(&g_ws_server_mutex);
589 m_send_message_queue.push(message);
590 pthread_mutex_unlock(&g_ws_server_mutex);
592 if (g_ws_server_context) {
593 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
595 LOGD("WARNING : g_ws_server_context is NULL");
599 void CWebHelperAgentWebSocket::on_focus_out(int ic)
602 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
603 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_FOCUS_OUT];
604 message.values.push_back(to_string(ic));
606 pthread_mutex_lock(&g_ws_server_mutex);
607 m_send_message_queue.push(message);
608 pthread_mutex_unlock(&g_ws_server_mutex);
610 if (g_ws_server_context) {
611 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
613 LOGD("WARNING : g_ws_server_context is NULL");
617 void CWebHelperAgentWebSocket::on_show(int ic)
620 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
621 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SHOW];
622 message.values.push_back(to_string(ic));
624 pthread_mutex_lock(&g_ws_server_mutex);
625 m_send_message_queue.push(message);
626 pthread_mutex_unlock(&g_ws_server_mutex);
628 if (g_ws_server_context) {
629 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
631 LOGD("WARNING : g_ws_server_context is NULL");
634 LOGD("put into send message buffer");
637 void CWebHelperAgentWebSocket::on_hide(int ic)
640 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
641 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_HIDE];
642 message.values.push_back(to_string(ic));
644 pthread_mutex_lock(&g_ws_server_mutex);
645 m_send_message_queue.push(message);
646 pthread_mutex_unlock(&g_ws_server_mutex);
648 if (g_ws_server_context) {
649 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
651 LOGD("WARNING : g_ws_server_context is NULL");
655 void CWebHelperAgentWebSocket::on_set_rotation(int degree)
658 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
659 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_ROTATION];
660 message.values.push_back(to_string(degree));
662 pthread_mutex_lock(&g_ws_server_mutex);
663 m_send_message_queue.push(message);
664 pthread_mutex_unlock(&g_ws_server_mutex);
666 if (g_ws_server_context) {
667 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
669 LOGD("WARNING : g_ws_server_context is NULL");
673 void CWebHelperAgentWebSocket::on_update_cursor_position(int ic, int cursor_pos)
676 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
677 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_CURSOR_POSITION];
678 message.values.push_back(to_string(ic));
679 message.values.push_back(to_string(cursor_pos));
681 pthread_mutex_lock(&g_ws_server_mutex);
682 m_send_message_queue.push(message);
683 pthread_mutex_unlock(&g_ws_server_mutex);
685 if (g_ws_server_context) {
686 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
688 LOGD("WARNING : g_ws_server_context is NULL");
692 void CWebHelperAgentWebSocket::on_update_surrounding_text(int ic, const char *text, int cursor)
695 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY];
696 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_SURROUNDING_TEXT];
697 message.values.push_back(to_string(cursor));
698 message.values.push_back(text);
700 pthread_mutex_lock(&g_ws_server_mutex);
701 m_send_message_queue.push(message);
702 pthread_mutex_unlock(&g_ws_server_mutex);
704 if (g_ws_server_context) {
705 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
707 LOGD("WARNING : g_ws_server_context is NULL");
711 void CWebHelperAgentWebSocket::on_update_selection(int ic, const char *text)
714 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY];
715 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_SELECTION];
716 message.values.push_back(text);
718 pthread_mutex_lock(&g_ws_server_mutex);
719 m_send_message_queue.push(message);
720 pthread_mutex_unlock(&g_ws_server_mutex);
722 if (g_ws_server_context) {
723 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
725 LOGD("WARNING : g_ws_server_context is NULL");
729 void CWebHelperAgentWebSocket::on_set_language(unsigned int language)
732 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
733 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_LANGUAGE];
736 for (unsigned int loop = 0;loop < sizeof(ISE_LANGUAGE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
737 if (language == (unsigned int)ISE_LANGUAGE_TYPES[loop].type_value) {
738 message.values.push_back(ISE_LANGUAGE_TYPES[loop].type_string);
744 pthread_mutex_lock(&g_ws_server_mutex);
745 m_send_message_queue.push(message);
746 pthread_mutex_unlock(&g_ws_server_mutex);
748 if (g_ws_server_context) {
749 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
751 LOGD("WARNING : g_ws_server_context is NULL");
756 void CWebHelperAgentWebSocket::on_set_imdata(char *buf, unsigned int len)
759 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
760 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_IMDATA];
761 message.values.push_back(buf);
763 pthread_mutex_lock(&g_ws_server_mutex);
764 m_send_message_queue.push(message);
765 pthread_mutex_unlock(&g_ws_server_mutex);
767 if (g_ws_server_context) {
768 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
770 LOGD("WARNING : g_ws_server_context is NULL");
774 void CWebHelperAgentWebSocket::on_get_imdata(char **buf, unsigned int *len)
777 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
778 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_IMDATA];
780 pthread_mutex_lock(&g_ws_server_mutex);
781 m_send_message_queue.push(message);
782 pthread_mutex_unlock(&g_ws_server_mutex);
784 if (g_ws_server_context) {
785 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
787 LOGD("WARNING : g_ws_server_context is NULL");
790 wait_for_reply_message();
792 std::vector<std::string> values;
793 /* Check if we received reply for GET_IMDATA message */
794 if (process_recved_messages_until_reply_found(
795 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_IMDATA], values)) {
796 if (values.size() > 0 && buf && len) {
797 int string_length = values.at(0).length();
798 (*buf) = new char[string_length + 1];
800 strncpy(*buf, values.at(0).c_str(), string_length);
801 /* Make sure this is a null-terminated string */
802 *(*buf + string_length) = '\0';
803 *len = string_length;
807 LOGD("process_recved_messages_until_reply_found returned FALSE");
809 /* Now process the rest in the recv buffer */
810 process_recved_messages();
813 void CWebHelperAgentWebSocket::on_set_return_key_type(unsigned int type)
816 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
817 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_RETURN_KEY_TYPE];
820 for (unsigned int loop = 0;loop < sizeof(ISE_RETURN_KEY_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
821 if (type == (unsigned int)ISE_RETURN_KEY_TYPES[loop].type_value) {
822 message.values.push_back(ISE_RETURN_KEY_TYPES[loop].type_string);
828 pthread_mutex_lock(&g_ws_server_mutex);
829 m_send_message_queue.push(message);
830 pthread_mutex_unlock(&g_ws_server_mutex);
832 if (g_ws_server_context) {
833 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
835 LOGD("WARNING : g_ws_server_context is NULL");
840 void CWebHelperAgentWebSocket::on_get_return_key_type(unsigned int *type)
843 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
844 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_TYPE];
846 pthread_mutex_lock(&g_ws_server_mutex);
847 m_send_message_queue.push(message);
848 pthread_mutex_unlock(&g_ws_server_mutex);
850 if (g_ws_server_context) {
851 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
853 LOGD("WARNING : g_ws_server_context is NULL");
856 wait_for_reply_message();
858 std::vector<std::string> values;
859 /* Check if we received reply for GET_RETURN_KEY_TYPE message */
860 if (process_recved_messages_until_reply_found(
861 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_TYPE], values)) {
863 for (unsigned int loop = 0;loop < sizeof(ISE_RETURN_KEY_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
864 if (values.at(0).compare(ISE_RETURN_KEY_TYPES[loop].type_string) == 0) {
865 *type = ISE_RETURN_KEY_TYPES[loop].type_value;
870 /* Now process the rest in the recv buffer */
871 process_recved_messages();
874 void CWebHelperAgentWebSocket::on_set_return_key_disable(unsigned int disabled)
877 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
878 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_RETURN_KEY_DISABLE];
881 for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
882 if (disabled == (unsigned int)ISE_TRUEFALSE_TYPES[loop].type_value) {
883 message.values.push_back(ISE_TRUEFALSE_TYPES[loop].type_string);
889 pthread_mutex_lock(&g_ws_server_mutex);
890 m_send_message_queue.push(message);
891 pthread_mutex_unlock(&g_ws_server_mutex);
893 if (g_ws_server_context) {
894 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
896 LOGD("WARNING : g_ws_server_context is NULL");
901 void CWebHelperAgentWebSocket::on_get_return_key_disable(unsigned int *disabled)
904 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
905 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_DISABLE];
907 pthread_mutex_lock(&g_ws_server_mutex);
908 m_send_message_queue.push(message);
909 pthread_mutex_unlock(&g_ws_server_mutex);
911 if (g_ws_server_context) {
912 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
914 LOGD("WARNING : g_ws_server_context is NULL");
917 wait_for_reply_message();
919 std::vector<std::string> values;
920 /* Check if we received reply for GET_RETURN_KEY_DISABLE message */
921 if (process_recved_messages_until_reply_found(
922 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_DISABLE], values)) {
924 for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
925 if (values.at(0).compare(ISE_TRUEFALSE_TYPES[loop].type_string) == 0) {
926 *disabled = ISE_TRUEFALSE_TYPES[loop].type_value;
931 /* Now process the rest in the recv buffer */
932 process_recved_messages();
935 void CWebHelperAgentWebSocket::on_set_layout(unsigned int layout)
938 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
939 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_LAYOUT];
942 for (unsigned int loop = 0;loop < sizeof(ISE_LAYOUT_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
943 if (layout == (unsigned int)ISE_LAYOUT_TYPES[loop].type_value) {
944 message.values.push_back(ISE_LAYOUT_TYPES[loop].type_string);
950 pthread_mutex_lock(&g_ws_server_mutex);
951 m_send_message_queue.push(message);
952 pthread_mutex_unlock(&g_ws_server_mutex);
954 if (g_ws_server_context) {
955 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
957 LOGD("WARNING : g_ws_server_context is NULL");
962 void CWebHelperAgentWebSocket::on_get_layout(unsigned int *layout)
965 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
966 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_LAYOUT];
968 pthread_mutex_lock(&g_ws_server_mutex);
969 m_send_message_queue.push(message);
970 pthread_mutex_unlock(&g_ws_server_mutex);
972 if (g_ws_server_context) {
973 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
975 LOGD("WARNING : g_ws_server_context is NULL");
978 wait_for_reply_message();
980 std::vector<std::string> values;
981 /* Check if we received reply for GET_LAYOUT message */
982 if (process_recved_messages_until_reply_found(
983 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_LAYOUT], values)) {
985 for (unsigned int loop = 0;loop < sizeof(ISE_LAYOUT_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
986 if (values.at(0).compare(ISE_LAYOUT_TYPES[loop].type_string) == 0) {
987 *layout = ISE_LAYOUT_TYPES[loop].type_value;
992 /* Now process the rest in the recv buffer */
993 process_recved_messages();
996 void CWebHelperAgentWebSocket::on_reset_input_context(int ic)
999 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
1000 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_RESET_INPUT_CONTEXT];
1001 message.values.push_back(to_string(ic));
1003 pthread_mutex_lock(&g_ws_server_mutex);
1004 m_send_message_queue.push(message);
1005 pthread_mutex_unlock(&g_ws_server_mutex);
1007 if (g_ws_server_context) {
1008 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
1010 LOGD("WARNING : g_ws_server_context is NULL");
1014 void CWebHelperAgentWebSocket::on_process_key_event(unsigned int code, unsigned int mask, unsigned int layout, unsigned int *ret)
1016 ISE_MESSAGE message;
1017 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
1018 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_PROCESS_KEY_EVENT];
1019 message.values.push_back(to_string(code));
1020 message.values.push_back(to_string(mask));
1021 message.values.push_back(to_string(layout));
1023 pthread_mutex_lock(&g_ws_server_mutex);
1024 m_send_message_queue.push(message);
1025 pthread_mutex_unlock(&g_ws_server_mutex);
1027 if (g_ws_server_context) {
1028 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
1030 LOGD("WARNING : g_ws_server_context is NULL");
1033 wait_for_reply_message();
1035 std::vector<std::string> values;
1036 /* Check if we received reply for PROCESS_KEY_EVENT message */
1037 if (process_recved_messages_until_reply_found(
1038 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_PROCESS_KEY_EVENT], values)) {
1040 for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
1041 if (values.at(0).compare(ISE_TRUEFALSE_TYPES[loop].type_string) == 0) {
1042 *ret = ISE_TRUEFALSE_TYPES[loop].type_value;
1047 /* Now process the rest in the recv buffer */
1048 process_recved_messages();
1051 CWebHelperAgentWebSocket* CWebHelperAgentWebSocket::get_current_instance()
1053 return m_current_instance;
1056 std::queue<ISE_MESSAGE>& CWebHelperAgentWebSocket::get_send_message_queue()
1058 return m_send_message_queue;
1061 std::queue<ISE_MESSAGE>& CWebHelperAgentWebSocket::get_recv_message_queue()
1063 return m_recv_message_queue;
1066 Ecore_Pipe* CWebHelperAgentWebSocket::get_message_pipe()
1068 return m_message_pipe;
1071 void CWebHelperAgentWebSocket::wait_for_reply_message()
1073 /* Let's wait for at most REPLY_TIMEOUT */
1075 struct timespec timeout;
1076 gettimeofday(&now, NULL);
1077 timeout.tv_sec = now.tv_sec + REPLY_TIMEOUT.tv_sec;
1078 timeout.tv_nsec = (now.tv_usec + REPLY_TIMEOUT.tv_usec) * 1000;
1079 pthread_mutex_lock(&g_ws_query_mutex);
1080 pthread_cond_timedwait(&g_ws_query_condition, &g_ws_query_mutex, &timeout);
1081 pthread_mutex_unlock(&g_ws_query_mutex);
1084 void CWebHelperAgentWebSocket::process_recved_messages()
1086 pthread_mutex_lock(&g_ws_server_mutex);
1088 while (m_recv_message_queue.size() > 0) {
1089 ISE_MESSAGE &message = m_recv_message_queue.front();
1091 handle_recved_message(message);
1093 m_recv_message_queue.pop();
1096 pthread_mutex_unlock(&g_ws_server_mutex);
1099 bool CWebHelperAgentWebSocket::process_recved_messages_until_reply_found(std::string command, std::vector<std::string> &values)
1103 pthread_mutex_lock(&g_ws_server_mutex);
1105 while (ret == false && m_recv_message_queue.size() > 0) {
1106 ISE_MESSAGE &message = m_recv_message_queue.front();
1108 if (message.command.compare(command) == 0 &&
1109 message.type.compare(ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY]) == 0) {
1111 values = message.values;
1113 handle_recved_message(message);
1115 m_recv_message_queue.pop();
1118 pthread_mutex_unlock(&g_ws_server_mutex);
1123 void CWebHelperAgentWebSocket::handle_recved_message(ISE_MESSAGE &message)
1125 static bool _key_event_processing = false;
1126 if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_LOG]) == 0) {
1127 std::string str = "";
1128 for (unsigned int loop = 0;loop < message.values.size();loop++) {
1129 str += message.values.at(loop).c_str();
1130 if (loop < message.values.size() - 1) {
1135 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_COMMIT_STRING]) == 0) {
1136 std::string str = "";
1137 for (unsigned int loop = 0;loop < message.values.size();loop++) {
1138 str += message.values.at(loop).c_str();
1139 if (loop < message.values.size() - 1) {
1143 if (_key_event_processing) {
1147 select(0, NULL, NULL, NULL, &tv);
1148 _key_event_processing = false;
1150 commit_string(str.c_str());
1151 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_PREEDIT_STRING]) == 0) {
1152 std::string str = "";
1153 for (unsigned int loop = 0;loop < message.values.size();loop++) {
1154 str += message.values.at(loop).c_str();
1155 if (loop < message.values.size() - 1) {
1159 update_preedit_string(str.c_str());
1160 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SEND_KEY_EVENT]) == 0) {
1161 if (message.values.size() == 1) {
1162 forward_key_event(atoi(message.values.at(0).c_str()));
1163 _key_event_processing = true;
1165 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES]) == 0) {
1166 LOGD("ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES");
1167 int portrait_width, portrait_height;
1168 int landscape_width, landscape_height;
1170 if (message.values.size() == 4 || message.values.size() == 2) {
1171 portrait_width = atoi(message.values.at(0).c_str());
1172 portrait_height = atoi(message.values.at(1).c_str());
1173 if (message.values.size() == 2) {
1174 landscape_width = portrait_width;
1175 landscape_height = portrait_height;
1177 landscape_width = atoi(message.values.at(2).c_str());
1178 landscape_height = atoi(message.values.at(3).c_str());
1181 LOGD("ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES : %d %d %d %d",
1182 portrait_width, portrait_height, landscape_width, landscape_height);
1185 /* Since the full screen IME makes the client application fully obscured,
1186 * when it hides the client receives resume command and try to show IME again.
1187 * So here we are adjusting the height value when the requested keyboard size
1188 * is the same with the screen size, as a workaround */
1189 int scr_w = 0, scr_h = 0;
1191 Ecore_Wl2_Display *ewd = NULL;
1192 if ((ewd = ecore_wl2_connected_display_get(NULL))) {
1193 ecore_wl2_display_screen_size_get(ewd, &scr_w, &scr_h);
1195 if (scr_w == portrait_width && scr_h == portrait_height) {
1196 portrait_height -= 1;
1198 if (scr_h == landscape_width && scr_w == landscape_height) {
1199 landscape_height -= 1;
1205 portrait_width, portrait_height, landscape_width, landscape_height);
1207 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_SELECTION]) == 0) {
1208 LOGD("ISE_MESSAGE_COMMAND_SET_SELECTION");
1209 if (message.values.size() == 2) {
1210 LOGD("ISE_MESSAGE_COMMAND_SET_SELECTION : %d %d",
1211 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1213 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1215 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_SELECTION]) == 0) {
1216 if (message.values.size() == 0) {
1219 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT]) == 0) {
1220 LOGD("ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT");
1221 if (message.values.size() == 2) {
1222 LOGD("ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT : %d %d",
1223 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1224 get_surrounding_text(
1225 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1227 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT]) == 0) {
1228 LOGD("ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT");
1229 if (message.values.size() == 2) {
1230 LOGD("ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT : %d %d",
1231 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1232 delete_surrounding_text(
1233 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1235 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_LOGIN]) == 0) {
1236 if (g_ws_server_context) {
1237 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
1239 LOGD("WARNING : g_ws_server_context is NULL");