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->initialized()) {
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 = std::string((const char *)in, len);
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 : [%zu] [%s], [%s]", len, 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;
452 info.options = LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND;
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 m_initialized = true;
480 bool CWebHelperAgentWebSocket::exit()
482 if (g_flush_server_recv_buffer_timer)
483 ecore_timer_del(g_flush_server_recv_buffer_timer);
484 g_flush_server_recv_buffer_timer = NULL;
488 g_ws_server_exit = true;
489 if (g_ws_server_context) {
490 lws_cancel_service(g_ws_server_context);
493 if (m_message_pipe) {
494 ecore_pipe_del(m_message_pipe);
495 m_message_pipe = NULL;
498 if (g_ws_server_thread) {
499 pthread_join(g_ws_server_thread, NULL);
502 pthread_cond_destroy(&g_ws_query_condition);
503 pthread_mutex_destroy(&g_ws_query_mutex);
505 pthread_mutex_destroy(&g_ws_server_mutex);
507 if (g_ws_server_context) {
508 lws_context_destroy(g_ws_server_context);
509 g_ws_server_context = NULL;
517 bool CWebHelperAgentWebSocket::run()
519 if (!m_initialized) {
520 LOGE("Not initialized");
525 if (g_ws_server_context) {
526 if (pthread_create(&g_ws_server_thread, NULL, &process_ws_server, NULL) != 0) {
527 g_ws_server_thread = (pthread_t)NULL;
532 test_client_connection();
535 LOGE("Failed creating server context : %p", g_ws_server_context);
541 void CWebHelperAgentWebSocket::signal(int sig)
547 std::string to_string(T i)
549 std::stringstream ss;
557 static void send_ise_message(std::queue<ISE_MESSAGE> *message_queue, ISE_MESSAGE message)
559 pthread_mutex_lock(&g_ws_server_mutex);
560 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_init()
573 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
574 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_INIT];
576 send_ise_message(&m_send_message_queue, message);
579 void CWebHelperAgentWebSocket::on_exit()
582 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
583 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_EXIT];
585 send_ise_message(&m_send_message_queue, message);
588 void CWebHelperAgentWebSocket::on_focus_in(int ic)
591 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
592 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_FOCUS_IN];
593 message.values.push_back(to_string(ic));
595 send_ise_message(&m_send_message_queue, message);
598 void CWebHelperAgentWebSocket::on_focus_out(int ic)
601 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
602 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_FOCUS_OUT];
603 message.values.push_back(to_string(ic));
605 send_ise_message(&m_send_message_queue, message);
608 void CWebHelperAgentWebSocket::on_show(int ic)
611 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
612 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SHOW];
613 message.values.push_back(to_string(ic));
615 send_ise_message(&m_send_message_queue, message);
617 LOGD("put into send message buffer");
620 void CWebHelperAgentWebSocket::on_hide(int ic)
623 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
624 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_HIDE];
625 message.values.push_back(to_string(ic));
627 send_ise_message(&m_send_message_queue, message);
630 void CWebHelperAgentWebSocket::on_set_rotation(int degree)
633 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
634 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_ROTATION];
635 message.values.push_back(to_string(degree));
637 send_ise_message(&m_send_message_queue, message);
640 void CWebHelperAgentWebSocket::on_update_cursor_position(int ic, int cursor_pos)
643 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
644 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_CURSOR_POSITION];
645 message.values.push_back(to_string(ic));
646 message.values.push_back(to_string(cursor_pos));
648 send_ise_message(&m_send_message_queue, message);
651 void CWebHelperAgentWebSocket::on_update_surrounding_text(int ic, const char *text, int cursor)
654 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY];
655 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_SURROUNDING_TEXT];
656 message.values.push_back(to_string(cursor));
657 message.values.push_back(text);
659 send_ise_message(&m_send_message_queue, message);
662 void CWebHelperAgentWebSocket::on_update_selection(int ic, const char *text)
665 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY];
666 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_SELECTION];
667 message.values.push_back(text);
669 send_ise_message(&m_send_message_queue, message);
672 void CWebHelperAgentWebSocket::on_set_language(unsigned int language)
675 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
676 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_LANGUAGE];
679 for (unsigned int loop = 0;loop < sizeof(ISE_LANGUAGE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
680 if (language == (unsigned int)ISE_LANGUAGE_TYPES[loop].type_value) {
681 message.values.push_back(ISE_LANGUAGE_TYPES[loop].type_string);
687 send_ise_message(&m_send_message_queue, message);
691 void CWebHelperAgentWebSocket::on_set_imdata(char *buf, unsigned int len)
694 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
695 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_IMDATA];
696 message.values.push_back(buf);
698 send_ise_message(&m_send_message_queue, message);
701 void CWebHelperAgentWebSocket::on_get_imdata(char **buf, unsigned int *len)
704 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
705 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_IMDATA];
707 send_ise_message(&m_send_message_queue, message);
709 wait_for_reply_message();
711 std::vector<std::string> values;
712 /* Check if we received reply for GET_IMDATA message */
713 if (process_recved_messages_until_reply_found(
714 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_IMDATA], values)) {
715 if (values.size() > 0 && buf && len) {
716 int string_length = values.at(0).length();
717 (*buf) = new char[string_length + 1];
719 strncpy(*buf, values.at(0).c_str(), string_length);
720 /* Make sure this is a null-terminated string */
721 *(*buf + string_length) = '\0';
722 *len = string_length;
726 LOGD("process_recved_messages_until_reply_found returned FALSE");
728 /* Now process the rest in the recv buffer */
729 process_recved_messages();
732 void CWebHelperAgentWebSocket::on_set_return_key_type(unsigned int type)
735 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
736 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_RETURN_KEY_TYPE];
739 for (unsigned int loop = 0;loop < sizeof(ISE_RETURN_KEY_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
740 if (type == (unsigned int)ISE_RETURN_KEY_TYPES[loop].type_value) {
741 message.values.push_back(ISE_RETURN_KEY_TYPES[loop].type_string);
747 send_ise_message(&m_send_message_queue, message);
751 void CWebHelperAgentWebSocket::on_get_return_key_type(unsigned int *type)
754 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
755 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_TYPE];
757 send_ise_message(&m_send_message_queue, message);
759 wait_for_reply_message();
761 std::vector<std::string> values;
762 /* Check if we received reply for GET_RETURN_KEY_TYPE message */
763 if (process_recved_messages_until_reply_found(
764 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_TYPE], values)) {
766 for (unsigned int loop = 0;loop < sizeof(ISE_RETURN_KEY_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
767 if (values.at(0).compare(ISE_RETURN_KEY_TYPES[loop].type_string) == 0) {
768 *type = ISE_RETURN_KEY_TYPES[loop].type_value;
773 /* Now process the rest in the recv buffer */
774 process_recved_messages();
777 void CWebHelperAgentWebSocket::on_set_return_key_disable(unsigned int disabled)
780 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
781 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_RETURN_KEY_DISABLE];
784 for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
785 if (disabled == (unsigned int)ISE_TRUEFALSE_TYPES[loop].type_value) {
786 message.values.push_back(ISE_TRUEFALSE_TYPES[loop].type_string);
792 send_ise_message(&m_send_message_queue, message);
796 void CWebHelperAgentWebSocket::on_get_return_key_disable(unsigned int *disabled)
799 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
800 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_DISABLE];
802 send_ise_message(&m_send_message_queue, message);
804 wait_for_reply_message();
806 std::vector<std::string> values;
807 /* Check if we received reply for GET_RETURN_KEY_DISABLE message */
808 if (process_recved_messages_until_reply_found(
809 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_DISABLE], values)) {
811 for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
812 if (values.at(0).compare(ISE_TRUEFALSE_TYPES[loop].type_string) == 0) {
813 *disabled = ISE_TRUEFALSE_TYPES[loop].type_value;
818 /* Now process the rest in the recv buffer */
819 process_recved_messages();
822 void CWebHelperAgentWebSocket::on_set_layout(unsigned int layout)
825 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
826 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_LAYOUT];
829 for (unsigned int loop = 0;loop < sizeof(ISE_LAYOUT_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
830 if (layout == (unsigned int)ISE_LAYOUT_TYPES[loop].type_value) {
831 message.values.push_back(ISE_LAYOUT_TYPES[loop].type_string);
837 send_ise_message(&m_send_message_queue, message);
841 void CWebHelperAgentWebSocket::on_get_layout(unsigned int *layout)
844 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
845 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_LAYOUT];
847 send_ise_message(&m_send_message_queue, message);
849 wait_for_reply_message();
851 std::vector<std::string> values;
852 /* Check if we received reply for GET_LAYOUT message */
853 if (process_recved_messages_until_reply_found(
854 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_LAYOUT], values)) {
856 for (unsigned int loop = 0;loop < sizeof(ISE_LAYOUT_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
857 if (values.at(0).compare(ISE_LAYOUT_TYPES[loop].type_string) == 0) {
858 *layout = ISE_LAYOUT_TYPES[loop].type_value;
863 /* Now process the rest in the recv buffer */
864 process_recved_messages();
867 void CWebHelperAgentWebSocket::on_reset_input_context(int ic)
870 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
871 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_RESET_INPUT_CONTEXT];
872 message.values.push_back(to_string(ic));
874 send_ise_message(&m_send_message_queue, message);
877 void CWebHelperAgentWebSocket::on_process_key_event(unsigned int code, unsigned int mask, unsigned int layout, unsigned int *ret)
880 message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
881 message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_PROCESS_KEY_EVENT];
882 message.values.push_back(to_string(code));
883 message.values.push_back(to_string(mask));
884 message.values.push_back(to_string(layout));
886 send_ise_message(&m_send_message_queue, message);
888 wait_for_reply_message();
890 std::vector<std::string> values;
891 /* Check if we received reply for PROCESS_KEY_EVENT message */
892 if (process_recved_messages_until_reply_found(
893 ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_PROCESS_KEY_EVENT], values)) {
895 for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
896 if (values.at(0).compare(ISE_TRUEFALSE_TYPES[loop].type_string) == 0) {
897 *ret = ISE_TRUEFALSE_TYPES[loop].type_value;
902 /* Now process the rest in the recv buffer */
903 process_recved_messages();
906 CWebHelperAgentWebSocket* CWebHelperAgentWebSocket::get_current_instance()
908 return m_current_instance;
911 std::queue<ISE_MESSAGE>& CWebHelperAgentWebSocket::get_send_message_queue()
913 return m_send_message_queue;
916 std::queue<ISE_MESSAGE>& CWebHelperAgentWebSocket::get_recv_message_queue()
918 return m_recv_message_queue;
921 Ecore_Pipe* CWebHelperAgentWebSocket::get_message_pipe()
923 return m_message_pipe;
926 void CWebHelperAgentWebSocket::wait_for_reply_message()
928 /* Let's wait for at most REPLY_TIMEOUT */
930 struct timespec timeout;
931 gettimeofday(&now, NULL);
932 timeout.tv_sec = now.tv_sec + REPLY_TIMEOUT.tv_sec;
933 timeout.tv_nsec = (now.tv_usec + REPLY_TIMEOUT.tv_usec) * 1000;
934 pthread_mutex_lock(&g_ws_query_mutex);
935 pthread_cond_timedwait(&g_ws_query_condition, &g_ws_query_mutex, &timeout);
936 pthread_mutex_unlock(&g_ws_query_mutex);
939 void CWebHelperAgentWebSocket::process_recved_messages()
941 pthread_mutex_lock(&g_ws_server_mutex);
943 while (m_recv_message_queue.size() > 0) {
944 ISE_MESSAGE &message = m_recv_message_queue.front();
946 handle_recved_message(message);
948 m_recv_message_queue.pop();
951 pthread_mutex_unlock(&g_ws_server_mutex);
954 bool CWebHelperAgentWebSocket::process_recved_messages_until_reply_found(std::string command, std::vector<std::string> &values)
958 pthread_mutex_lock(&g_ws_server_mutex);
960 while (ret == false && m_recv_message_queue.size() > 0) {
961 ISE_MESSAGE &message = m_recv_message_queue.front();
963 if (message.command.compare(command) == 0 &&
964 message.type.compare(ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY]) == 0) {
966 values = message.values;
968 handle_recved_message(message);
970 m_recv_message_queue.pop();
973 pthread_mutex_unlock(&g_ws_server_mutex);
978 void CWebHelperAgentWebSocket::handle_recved_message(ISE_MESSAGE &message)
980 static bool _key_event_processing = false;
981 if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_LOG]) == 0) {
982 std::string str = "";
983 for (unsigned int loop = 0;loop < message.values.size();loop++) {
984 str += message.values.at(loop).c_str();
985 if (loop < message.values.size() - 1) {
990 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_COMMIT_STRING]) == 0) {
991 std::string str = "";
992 for (unsigned int loop = 0;loop < message.values.size();loop++) {
993 str += message.values.at(loop).c_str();
994 if (loop < message.values.size() - 1) {
998 if (_key_event_processing) {
1002 select(0, NULL, NULL, NULL, &tv);
1003 _key_event_processing = false;
1005 commit_string(str.c_str());
1006 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_PREEDIT_STRING]) == 0) {
1007 std::string str = "";
1008 for (unsigned int loop = 0;loop < message.values.size();loop++) {
1009 str += message.values.at(loop).c_str();
1010 if (loop < message.values.size() - 1) {
1014 update_preedit_string(str.c_str());
1015 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SEND_KEY_EVENT]) == 0) {
1016 if (message.values.size() == 1) {
1017 forward_key_event(atoi(message.values.at(0).c_str()));
1018 _key_event_processing = true;
1020 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES]) == 0) {
1021 LOGD("ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES");
1022 int portrait_width, portrait_height;
1023 int landscape_width, landscape_height;
1025 if (message.values.size() == 4 || message.values.size() == 2) {
1026 portrait_width = atoi(message.values.at(0).c_str());
1027 portrait_height = atoi(message.values.at(1).c_str());
1028 if (message.values.size() == 2) {
1029 landscape_width = portrait_width;
1030 landscape_height = portrait_height;
1032 landscape_width = atoi(message.values.at(2).c_str());
1033 landscape_height = atoi(message.values.at(3).c_str());
1036 LOGD("ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES : %d %d %d %d",
1037 portrait_width, portrait_height, landscape_width, landscape_height);
1040 /* Since the full screen IME makes the client application fully obscured,
1041 * when it hides the client receives resume command and try to show IME again.
1042 * So here we are adjusting the height value when the requested keyboard size
1043 * is the same with the screen size, as a workaround */
1044 int scr_w = 0, scr_h = 0;
1046 Ecore_Wl2_Display *ewd = NULL;
1047 if ((ewd = ecore_wl2_connected_display_get(NULL))) {
1048 ecore_wl2_display_screen_size_get(ewd, &scr_w, &scr_h);
1050 if (scr_w == portrait_width && scr_h == portrait_height) {
1051 portrait_height -= 1;
1053 if (scr_h == landscape_width && scr_w == landscape_height) {
1054 landscape_height -= 1;
1060 portrait_width, portrait_height, landscape_width, landscape_height);
1062 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_SELECTION]) == 0) {
1063 LOGD("ISE_MESSAGE_COMMAND_SET_SELECTION");
1064 if (message.values.size() == 2) {
1065 LOGD("ISE_MESSAGE_COMMAND_SET_SELECTION : %d %d",
1066 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1068 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1070 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_SELECTION]) == 0) {
1071 if (message.values.size() == 0) {
1074 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT]) == 0) {
1075 LOGD("ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT");
1076 if (message.values.size() == 2) {
1077 LOGD("ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT : %d %d",
1078 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1079 get_surrounding_text(
1080 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1082 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT]) == 0) {
1083 LOGD("ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT");
1084 if (message.values.size() == 2) {
1085 LOGD("ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT : %d %d",
1086 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1087 delete_surrounding_text(
1088 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1090 } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_LOGIN]) == 0) {
1091 if (g_ws_server_context) {
1092 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
1094 LOGD("WARNING : g_ws_server_context is NULL");