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)
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_timer_del(g_flush_server_recv_buffer_timer);
341 std::string str = (const char *)in;
342 if (CISEMessageSerializer::valid(str)) {
343 LOGD("A valid new message received, flush previous buffer");
344 flush_server_recv_buffer_func((void*)pss);
347 pthread_mutex_lock(&g_ws_server_mutex);
348 server_recv_buffer += str;
349 pthread_mutex_unlock(&g_ws_server_mutex);
351 g_flush_server_recv_buffer_timer = ecore_timer_add(0.05, flush_server_recv_buffer_func, (void*)pss);
362 void *process_ws_server(void *data)
364 while (!force_exit && !g_ws_server_exit) {
366 gettimeofday(&tv, NULL);
368 if (g_ws_server_context) {
369 lws_service(g_ws_server_context, 50);
371 LOGD("WARNING : g_ws_server_context is NULL");
377 void log_func(int level, const char *line)
380 LOGD("LEVEL : %d , %s", level, line);
384 CWebHelperAgentWebSocket::CWebHelperAgentWebSocket()
386 if (m_current_instance != NULL) {
387 LOGD("WARNING : m_current_instance is NOT NULL");
389 m_current_instance = this;
390 m_message_pipe = NULL;
391 m_initialized = false;
394 CWebHelperAgentWebSocket::~CWebHelperAgentWebSocket()
396 if (m_current_instance == this) {
397 m_current_instance = NULL;
400 if (m_message_pipe) {
401 ecore_pipe_del(m_message_pipe);
402 m_message_pipe = NULL;
406 static void message_pipe_handler(void *data, void *buffer, unsigned int nbyte)
409 if (strncmp((const char*)buffer, RECVED_MESSAGE, strlen(RECVED_MESSAGE)) == 0) {
410 CWebHelperAgentWebSocket *agent = CWebHelperAgentWebSocket::get_current_instance();
412 agent->process_recved_messages();
415 if (g_ws_server_context) {
416 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
418 LOGD("WARNING : g_ws_server_context is NULL");
424 bool CWebHelperAgentWebSocket::init()
428 struct lws_context_creation_info info;
430 memset(&info, 0, sizeof info);
431 info.port = WEBSOCKET_PORT;
433 int log_level = LLL_ERR | LLL_WARN | LLL_DEBUG;
434 lws_set_log_level(log_level, log_func);
437 info.protocols = protocols;
438 info.extensions = NULL;
439 info.ssl_cert_filepath = NULL;
440 info.ssl_private_key_filepath = NULL;
447 /* The WebSocket server is running on a separate thread, and let the thread send a message
448 through this pipe to guarantee thread safety */
449 m_message_pipe = ecore_pipe_add(message_pipe_handler, NULL);
451 /* Let's retry creating server context for a certain number of times */
452 const int max_retry_num = 30;
456 g_ws_server_context = lws_create_context(&info);
457 LOGD("libwebsocket context : %p", g_ws_server_context);
459 } while (g_ws_server_context == NULL && retry_num++ < max_retry_num);
461 pthread_mutex_init(&g_ws_server_mutex, NULL);
463 pthread_mutex_init(&g_ws_query_mutex, NULL);
464 pthread_cond_init(&g_ws_query_condition, NULL);
466 if (g_ws_server_context) {
467 if (pthread_create(&g_ws_server_thread, NULL, &process_ws_server, NULL) != 0) {
468 g_ws_server_thread = (pthread_t)NULL;
472 LOGE("Failed creating server context : %p", g_ws_server_context);
478 m_initialized = true;
480 test_client_connection();
485 bool CWebHelperAgentWebSocket::exit()
487 if (g_flush_server_recv_buffer_timer)
488 ecore_timer_del(g_flush_server_recv_buffer_timer);
489 g_flush_server_recv_buffer_timer = NULL;
493 g_ws_server_exit = true;
495 if (m_message_pipe) {
496 ecore_pipe_del(m_message_pipe);
497 m_message_pipe = NULL;
500 if (g_ws_server_thread) {
501 pthread_join(g_ws_server_thread, NULL);
504 pthread_cond_destroy(&g_ws_query_condition);
505 pthread_mutex_destroy(&g_ws_query_mutex);
507 pthread_mutex_destroy(&g_ws_server_mutex);
509 if (g_ws_server_context) {
510 lws_cancel_service(g_ws_server_context);
511 lws_context_destroy(g_ws_server_context);
512 g_ws_server_context = NULL;
520 void CWebHelperAgentWebSocket::signal(int sig)
526 std::string to_string(T i)
528 std::stringstream ss;
536 void CWebHelperAgentWebSocket::on_init()
539 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
540 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_INIT];
542 pthread_mutex_lock(&g_ws_server_mutex);
543 m_send_message_queue.push(message);
544 pthread_mutex_unlock(&g_ws_server_mutex);
546 if (g_ws_server_context) {
547 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
549 LOGD("WARNING : g_ws_server_context is NULL");
553 void CWebHelperAgentWebSocket::on_exit()
556 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
557 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_EXIT];
559 pthread_mutex_lock(&g_ws_server_mutex);
560 m_send_message_queue.push(message);
561 pthread_mutex_unlock(&g_ws_server_mutex);
563 if (g_ws_server_context) {
564 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
566 LOGD("WARNING : g_ws_server_context is NULL");
570 void CWebHelperAgentWebSocket::on_focus_in(int ic)
573 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
574 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_FOCUS_IN];
575 message.values.push_back(to_string(ic));
577 pthread_mutex_lock(&g_ws_server_mutex);
578 m_send_message_queue.push(message);
579 pthread_mutex_unlock(&g_ws_server_mutex);
581 if (g_ws_server_context) {
582 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
584 LOGD("WARNING : g_ws_server_context is NULL");
588 void CWebHelperAgentWebSocket::on_focus_out(int ic)
591 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
592 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_FOCUS_OUT];
593 message.values.push_back(to_string(ic));
595 pthread_mutex_lock(&g_ws_server_mutex);
596 m_send_message_queue.push(message);
597 pthread_mutex_unlock(&g_ws_server_mutex);
599 if (g_ws_server_context) {
600 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
602 LOGD("WARNING : g_ws_server_context is NULL");
606 void CWebHelperAgentWebSocket::on_show(int ic)
609 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
610 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SHOW];
611 message.values.push_back(to_string(ic));
613 pthread_mutex_lock(&g_ws_server_mutex);
614 m_send_message_queue.push(message);
615 pthread_mutex_unlock(&g_ws_server_mutex);
617 if (g_ws_server_context) {
618 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
620 LOGD("WARNING : g_ws_server_context is NULL");
623 LOGD("put into send message buffer");
626 void CWebHelperAgentWebSocket::on_hide(int ic)
629 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
630 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_HIDE];
631 message.values.push_back(to_string(ic));
633 pthread_mutex_lock(&g_ws_server_mutex);
634 m_send_message_queue.push(message);
635 pthread_mutex_unlock(&g_ws_server_mutex);
637 if (g_ws_server_context) {
638 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
640 LOGD("WARNING : g_ws_server_context is NULL");
644 void CWebHelperAgentWebSocket::on_set_rotation(int degree)
647 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
648 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_ROTATION];
649 message.values.push_back(to_string(degree));
651 pthread_mutex_lock(&g_ws_server_mutex);
652 m_send_message_queue.push(message);
653 pthread_mutex_unlock(&g_ws_server_mutex);
655 if (g_ws_server_context) {
656 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
658 LOGD("WARNING : g_ws_server_context is NULL");
662 void CWebHelperAgentWebSocket::on_update_cursor_position(int ic, int cursor_pos)
665 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
666 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_CURSOR_POSITION];
667 message.values.push_back(to_string(ic));
668 message.values.push_back(to_string(cursor_pos));
670 pthread_mutex_lock(&g_ws_server_mutex);
671 m_send_message_queue.push(message);
672 pthread_mutex_unlock(&g_ws_server_mutex);
674 if (g_ws_server_context) {
675 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
677 LOGD("WARNING : g_ws_server_context is NULL");
681 void CWebHelperAgentWebSocket::on_update_surrounding_text(int ic, const char *text, int cursor)
684 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY];
685 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_SURROUNDING_TEXT];
686 message.values.push_back(to_string(cursor));
687 message.values.push_back(text);
689 pthread_mutex_lock(&g_ws_server_mutex);
690 m_send_message_queue.push(message);
691 pthread_mutex_unlock(&g_ws_server_mutex);
693 if (g_ws_server_context) {
694 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
696 LOGD("WARNING : g_ws_server_context is NULL");
700 void CWebHelperAgentWebSocket::on_update_selection(int ic, const char *text)
703 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY];
704 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_SELECTION];
705 message.values.push_back(text);
707 pthread_mutex_lock(&g_ws_server_mutex);
708 m_send_message_queue.push(message);
709 pthread_mutex_unlock(&g_ws_server_mutex);
711 if (g_ws_server_context) {
712 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
714 LOGD("WARNING : g_ws_server_context is NULL");
718 void CWebHelperAgentWebSocket::on_set_language(unsigned int language)
721 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
722 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_LANGUAGE];
725 for (unsigned int loop = 0;loop < sizeof(ISE_LANGUAGE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
726 if (language == (unsigned int)ISE_LANGUAGE_TYPES[loop].type_value) {
727 message.values.push_back(ISE_LANGUAGE_TYPES[loop].type_string);
733 pthread_mutex_lock(&g_ws_server_mutex);
734 m_send_message_queue.push(message);
735 pthread_mutex_unlock(&g_ws_server_mutex);
737 if (g_ws_server_context) {
738 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
740 LOGD("WARNING : g_ws_server_context is NULL");
745 void CWebHelperAgentWebSocket::on_set_imdata(char *buf, unsigned int len)
748 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
749 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_IMDATA];
750 message.values.push_back(buf);
752 pthread_mutex_lock(&g_ws_server_mutex);
753 m_send_message_queue.push(message);
754 pthread_mutex_unlock(&g_ws_server_mutex);
756 if (g_ws_server_context) {
757 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
759 LOGD("WARNING : g_ws_server_context is NULL");
763 void CWebHelperAgentWebSocket::on_get_imdata(char **buf, unsigned int *len)
766 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
767 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_IMDATA];
769 pthread_mutex_lock(&g_ws_server_mutex);
770 m_send_message_queue.push(message);
771 pthread_mutex_unlock(&g_ws_server_mutex);
773 if (g_ws_server_context) {
774 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
776 LOGD("WARNING : g_ws_server_context is NULL");
779 wait_for_reply_message();
781 std::vector<std::string> values;
782 /* Check if we received reply for GET_IMDATA message */
783 if (process_recved_messages_until_reply_found(
784 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_IMDATA], values)) {
785 if (values.size() > 0 && buf && len) {
786 int string_length = values.at(0).length();
787 (*buf) = new char[string_length + 1];
789 strncpy(*buf, values.at(0).c_str(), string_length);
790 /* Make sure this is a null-terminated string */
791 *(*buf + string_length) = '\0';
792 *len = string_length;
796 LOGD("process_recved_messages_until_reply_found returned FALSE");
798 /* Now process the rest in the recv buffer */
799 process_recved_messages();
802 void CWebHelperAgentWebSocket::on_set_return_key_type(unsigned int type)
805 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
806 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_RETURN_KEY_TYPE];
809 for (unsigned int loop = 0;loop < sizeof(ISE_RETURN_KEY_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
810 if (type == (unsigned int)ISE_RETURN_KEY_TYPES[loop].type_value) {
811 message.values.push_back(ISE_RETURN_KEY_TYPES[loop].type_string);
817 pthread_mutex_lock(&g_ws_server_mutex);
818 m_send_message_queue.push(message);
819 pthread_mutex_unlock(&g_ws_server_mutex);
821 if (g_ws_server_context) {
822 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
824 LOGD("WARNING : g_ws_server_context is NULL");
829 void CWebHelperAgentWebSocket::on_get_return_key_type(unsigned int *type)
832 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
833 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_TYPE];
835 pthread_mutex_lock(&g_ws_server_mutex);
836 m_send_message_queue.push(message);
837 pthread_mutex_unlock(&g_ws_server_mutex);
839 if (g_ws_server_context) {
840 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
842 LOGD("WARNING : g_ws_server_context is NULL");
845 wait_for_reply_message();
847 std::vector<std::string> values;
848 /* Check if we received reply for GET_RETURN_KEY_TYPE message */
849 if (process_recved_messages_until_reply_found(
850 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_TYPE], values)) {
852 for (unsigned int loop = 0;loop < sizeof(ISE_RETURN_KEY_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
853 if (values.at(0).compare(ISE_RETURN_KEY_TYPES[loop].type_string) == 0) {
854 *type = ISE_RETURN_KEY_TYPES[loop].type_value;
859 /* Now process the rest in the recv buffer */
860 process_recved_messages();
863 void CWebHelperAgentWebSocket::on_set_return_key_disable(unsigned int disabled)
866 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
867 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_RETURN_KEY_DISABLE];
870 for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
871 if (disabled == (unsigned int)ISE_TRUEFALSE_TYPES[loop].type_value) {
872 message.values.push_back(ISE_TRUEFALSE_TYPES[loop].type_string);
878 pthread_mutex_lock(&g_ws_server_mutex);
879 m_send_message_queue.push(message);
880 pthread_mutex_unlock(&g_ws_server_mutex);
882 if (g_ws_server_context) {
883 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
885 LOGD("WARNING : g_ws_server_context is NULL");
890 void CWebHelperAgentWebSocket::on_get_return_key_disable(unsigned int *disabled)
893 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
894 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_DISABLE];
896 pthread_mutex_lock(&g_ws_server_mutex);
897 m_send_message_queue.push(message);
898 pthread_mutex_unlock(&g_ws_server_mutex);
900 if (g_ws_server_context) {
901 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
903 LOGD("WARNING : g_ws_server_context is NULL");
906 wait_for_reply_message();
908 std::vector<std::string> values;
909 /* Check if we received reply for GET_RETURN_KEY_DISABLE message */
910 if (process_recved_messages_until_reply_found(
911 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_DISABLE], values)) {
913 for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
914 if (values.at(0).compare(ISE_TRUEFALSE_TYPES[loop].type_string) == 0) {
915 *disabled = ISE_TRUEFALSE_TYPES[loop].type_value;
920 /* Now process the rest in the recv buffer */
921 process_recved_messages();
924 void CWebHelperAgentWebSocket::on_set_layout(unsigned int layout)
927 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
928 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_LAYOUT];
931 for (unsigned int loop = 0;loop < sizeof(ISE_LAYOUT_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
932 if (layout == (unsigned int)ISE_LAYOUT_TYPES[loop].type_value) {
933 message.values.push_back(ISE_LAYOUT_TYPES[loop].type_string);
939 pthread_mutex_lock(&g_ws_server_mutex);
940 m_send_message_queue.push(message);
941 pthread_mutex_unlock(&g_ws_server_mutex);
943 if (g_ws_server_context) {
944 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
946 LOGD("WARNING : g_ws_server_context is NULL");
951 void CWebHelperAgentWebSocket::on_get_layout(unsigned int *layout)
954 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
955 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_LAYOUT];
957 pthread_mutex_lock(&g_ws_server_mutex);
958 m_send_message_queue.push(message);
959 pthread_mutex_unlock(&g_ws_server_mutex);
961 if (g_ws_server_context) {
962 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
964 LOGD("WARNING : g_ws_server_context is NULL");
967 wait_for_reply_message();
969 std::vector<std::string> values;
970 /* Check if we received reply for GET_LAYOUT message */
971 if (process_recved_messages_until_reply_found(
972 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_LAYOUT], values)) {
974 for (unsigned int loop = 0;loop < sizeof(ISE_LAYOUT_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
975 if (values.at(0).compare(ISE_LAYOUT_TYPES[loop].type_string) == 0) {
976 *layout = ISE_LAYOUT_TYPES[loop].type_value;
981 /* Now process the rest in the recv buffer */
982 process_recved_messages();
985 void CWebHelperAgentWebSocket::on_reset_input_context(int ic)
988 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
989 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_RESET_INPUT_CONTEXT];
990 message.values.push_back(to_string(ic));
992 pthread_mutex_lock(&g_ws_server_mutex);
993 m_send_message_queue.push(message);
994 pthread_mutex_unlock(&g_ws_server_mutex);
996 if (g_ws_server_context) {
997 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
999 LOGD("WARNING : g_ws_server_context is NULL");
1003 void CWebHelperAgentWebSocket::on_process_key_event(unsigned int code, unsigned int mask, unsigned int layout, unsigned int *ret)
1005 ISE_MESSAGE message;
1006 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
1007 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_PROCESS_KEY_EVENT];
1008 message.values.push_back(to_string(code));
1009 message.values.push_back(to_string(mask));
1010 message.values.push_back(to_string(layout));
1012 pthread_mutex_lock(&g_ws_server_mutex);
1013 m_send_message_queue.push(message);
1014 pthread_mutex_unlock(&g_ws_server_mutex);
1016 if (g_ws_server_context) {
1017 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
1019 LOGD("WARNING : g_ws_server_context is NULL");
1022 wait_for_reply_message();
1024 std::vector<std::string> values;
1025 /* Check if we received reply for PROCESS_KEY_EVENT message */
1026 if (process_recved_messages_until_reply_found(
1027 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_PROCESS_KEY_EVENT], values)) {
1029 for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
1030 if (values.at(0).compare(ISE_TRUEFALSE_TYPES[loop].type_string) == 0) {
1031 *ret = ISE_TRUEFALSE_TYPES[loop].type_value;
1036 /* Now process the rest in the recv buffer */
1037 process_recved_messages();
1040 CWebHelperAgentWebSocket* CWebHelperAgentWebSocket::get_current_instance()
1042 return m_current_instance;
1045 std::queue<ISE_MESSAGE>& CWebHelperAgentWebSocket::get_send_message_queue()
1047 return m_send_message_queue;
1050 std::queue<ISE_MESSAGE>& CWebHelperAgentWebSocket::get_recv_message_queue()
1052 return m_recv_message_queue;
1055 Ecore_Pipe* CWebHelperAgentWebSocket::get_message_pipe()
1057 return m_message_pipe;
1060 void CWebHelperAgentWebSocket::wait_for_reply_message()
1062 /* Let's wait for at most REPLY_TIMEOUT */
1064 struct timespec timeout;
1065 gettimeofday(&now, NULL);
1066 timeout.tv_sec = now.tv_sec + REPLY_TIMEOUT.tv_sec;
1067 timeout.tv_nsec = (now.tv_usec + REPLY_TIMEOUT.tv_usec) * 1000;
1068 pthread_mutex_lock(&g_ws_query_mutex);
1069 pthread_cond_timedwait(&g_ws_query_condition, &g_ws_query_mutex, &timeout);
1070 pthread_mutex_unlock(&g_ws_query_mutex);
1073 void CWebHelperAgentWebSocket::process_recved_messages()
1075 pthread_mutex_lock(&g_ws_server_mutex);
1077 while (m_recv_message_queue.size() > 0) {
1078 ISE_MESSAGE &message = m_recv_message_queue.front();
1080 handle_recved_message(message);
1082 m_recv_message_queue.pop();
1085 pthread_mutex_unlock(&g_ws_server_mutex);
1088 bool CWebHelperAgentWebSocket::process_recved_messages_until_reply_found(std::string command, std::vector<std::string> &values)
1092 pthread_mutex_lock(&g_ws_server_mutex);
1094 while (ret == false && m_recv_message_queue.size() > 0) {
1095 ISE_MESSAGE &message = m_recv_message_queue.front();
1097 if (message.command.compare(command) == 0 &&
1098 message.type.compare(ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY]) == 0) {
1100 values = message.values;
1102 handle_recved_message(message);
1104 m_recv_message_queue.pop();
1107 pthread_mutex_unlock(&g_ws_server_mutex);
1112 void CWebHelperAgentWebSocket::handle_recved_message(ISE_MESSAGE &message)
1114 static bool _key_event_processing = false;
1115 if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_LOG]) == 0) {
1116 std::string str = "";
1117 for (unsigned int loop = 0;loop < message.values.size();loop++) {
1118 str += message.values.at(loop).c_str();
1119 if (loop < message.values.size() - 1) {
1124 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_COMMIT_STRING]) == 0) {
1125 std::string str = "";
1126 for (unsigned int loop = 0;loop < message.values.size();loop++) {
1127 str += message.values.at(loop).c_str();
1128 if (loop < message.values.size() - 1) {
1132 if (_key_event_processing) {
1136 select(0, NULL, NULL, NULL, &tv);
1137 _key_event_processing = false;
1139 commit_string(str.c_str());
1140 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_PREEDIT_STRING]) == 0) {
1141 std::string str = "";
1142 for (unsigned int loop = 0;loop < message.values.size();loop++) {
1143 str += message.values.at(loop).c_str();
1144 if (loop < message.values.size() - 1) {
1148 update_preedit_string(str.c_str());
1149 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SEND_KEY_EVENT]) == 0) {
1150 if (message.values.size() == 1) {
1151 forward_key_event(atoi(message.values.at(0).c_str()));
1152 _key_event_processing = true;
1154 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES]) == 0) {
1155 LOGD("ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES");
1156 int portrait_width, portrait_height;
1157 int landscape_width, landscape_height;
1159 if (message.values.size() == 4 || message.values.size() == 2) {
1160 portrait_width = atoi(message.values.at(0).c_str());
1161 portrait_height = atoi(message.values.at(1).c_str());
1162 if (message.values.size() == 2) {
1163 landscape_width = portrait_width;
1164 landscape_height = portrait_height;
1166 landscape_width = atoi(message.values.at(2).c_str());
1167 landscape_height = atoi(message.values.at(3).c_str());
1170 LOGD("ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES : %d %d %d %d",
1171 portrait_width, portrait_height, landscape_width, landscape_height);
1174 /* Since the full screen IME makes the client application fully obscured,
1175 * when it hides the client receives resume command and try to show IME again.
1176 * So here we are adjusting the height value when the requested keyboard size
1177 * is the same with the screen size, as a workaround */
1178 int scr_w = 0, scr_h = 0;
1180 Ecore_Wl2_Display *ewd = NULL;
1181 if ((ewd = ecore_wl2_connected_display_get(NULL))) {
1182 ecore_wl2_display_screen_size_get(ewd, &scr_w, &scr_h);
1184 if (scr_w == portrait_width && scr_h == portrait_height) {
1185 portrait_height -= 1;
1187 if (scr_h == landscape_width && scr_w == landscape_height) {
1188 landscape_height -= 1;
1194 portrait_width, portrait_height, landscape_width, landscape_height);
1196 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_SELECTION]) == 0) {
1197 LOGD("ISE_MESSAGE_COMMAND_SET_SELECTION");
1198 if (message.values.size() == 2) {
1199 LOGD("ISE_MESSAGE_COMMAND_SET_SELECTION : %d %d",
1200 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1202 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1204 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_SELECTION]) == 0) {
1205 if (message.values.size() == 0) {
1208 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT]) == 0) {
1209 LOGD("ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT");
1210 if (message.values.size() == 2) {
1211 LOGD("ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT : %d %d",
1212 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1213 get_surrounding_text(
1214 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1216 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT]) == 0) {
1217 LOGD("ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT");
1218 if (message.values.size() == 2) {
1219 LOGD("ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT : %d %d",
1220 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1221 delete_surrounding_text(
1222 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1224 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_LOGIN]) == 0) {
1225 if (g_ws_server_context) {
1226 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
1228 LOGD("WARNING : g_ws_server_context is NULL");