diff options
-rw-r--r-- | client/application.cpp | 7 | ||||
-rw-r--r-- | client/application.h | 1 | ||||
-rw-r--r-- | client/inputs_handler.h | 1 | ||||
-rw-r--r-- | client/red_window.h | 1 | ||||
-rw-r--r-- | client/screen.cpp | 5 | ||||
-rw-r--r-- | client/screen.h | 1 | ||||
-rw-r--r-- | client/windows/red_window.cpp | 53 | ||||
-rw-r--r-- | client/x11/platform.cpp | 31 | ||||
-rw-r--r-- | client/x11/red_window.cpp | 48 | ||||
-rw-r--r-- | client/x11/red_window_p.h | 1 | ||||
-rw-r--r-- | client/x11/x_platform.h | 1 |
11 files changed, 147 insertions, 3 deletions
diff --git a/client/application.cpp b/client/application.cpp index 943fb3b7..8345f390 100644 --- a/client/application.cpp +++ b/client/application.cpp @@ -907,7 +907,12 @@ void Application::on_key_up(RedKey key) } do_on_key_up(key); - } +} + +void Application::on_char(uint32_t ch) +{ + _key_handler->on_char(ch); +} void Application::on_deactivate_screen(RedScreen* screen) { diff --git a/client/application.h b/client/application.h index ebce51a4..da2d4218 100644 --- a/client/application.h +++ b/client/application.h @@ -131,6 +131,7 @@ public: void on_mouse_up(int button, int buttons_state); void on_key_down(RedKey key); void on_key_up(RedKey key); + void on_char(uint32_t ch); void on_deactivate_screen(RedScreen* screen); void on_activate_screen(RedScreen* screen); void on_start_screen_key_interception(RedScreen* screen); diff --git a/client/inputs_handler.h b/client/inputs_handler.h index de72806b..33a74d61 100644 --- a/client/inputs_handler.h +++ b/client/inputs_handler.h @@ -25,6 +25,7 @@ public: virtual ~KeyHandler() {} virtual void on_key_down(RedKey key) {} virtual void on_key_up(RedKey key) {} + virtual void on_char(uint32_t ch) {} virtual void on_focus_in() {} virtual void on_focus_out() {} virtual bool permit_focus_loss() { return true;} diff --git a/client/red_window.h b/client/red_window.h index 4b1d2443..3c7ac77b 100644 --- a/client/red_window.h +++ b/client/red_window.h @@ -126,6 +126,7 @@ public: virtual void on_key_press(RedKey key) = 0; virtual void on_key_release(RedKey key) = 0; + virtual void on_char(uint32_t ch) = 0; virtual void on_deactivate() = 0; virtual void on_activate() = 0; diff --git a/client/screen.cpp b/client/screen.cpp index 50905710..39c9dbc5 100644 --- a/client/screen.cpp +++ b/client/screen.cpp @@ -659,6 +659,11 @@ void RedScreen::on_key_release(RedKey key) _owner.on_key_up(key); } +void RedScreen::on_char(uint32_t ch) +{ + _owner.on_char(ch); +} + void RedScreen::on_deactivate() { relase_mouse(); diff --git a/client/screen.h b/client/screen.h index 10c8fd4f..3321c2ff 100644 --- a/client/screen.h +++ b/client/screen.h @@ -138,6 +138,7 @@ private: virtual void on_key_press(RedKey key); virtual void on_key_release(RedKey key); + virtual void on_char(uint32_t ch); virtual void on_deactivate(); virtual void on_activate(); diff --git a/client/windows/red_window.cpp b/client/windows/red_window.cpp index 5c2d2da1..8716b073 100644 --- a/client/windows/red_window.cpp +++ b/client/windows/red_window.cpp @@ -94,6 +94,46 @@ static inline void send_filtered_keys(RedWindow* window) filtered_up_keys.clear(); } +static inline bool is_high_surrogate(uint32_t val) +{ + return val >= 0xd800 && val <= 0xdbff; +} + +static inline bool is_low_surrogate(uint32_t val) +{ + return val >= 0xdc00 && val <= 0xdfff; +} + +static uint32_t utf16_to_utf32(uint16_t*& utf16, int& len) +{ + if (!len) { + return 0; + } + + uint32_t val = utf16[0]; + + if (!is_high_surrogate(val)) { + utf16++; + len--; + return val; + } + + if (len < 2) { + THROW("partial char"); + } + + uint32_t val2 = utf16[1]; + + if (!is_low_surrogate(val2)) { + THROW("invalid sequence"); + } + + utf16 += 2; + len -= 2; + + return (((val & 0x3ff) << 10) | (val2 & 0x3ff)) + 0x10000; +} + LRESULT CALLBACK RedWindow_p::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { RedWindow* window = (RedWindow*)GetWindowLong(hWnd, GWL_USERDATA); @@ -177,6 +217,19 @@ LRESULT CALLBACK RedWindow_p::WindowProc(HWND hWnd, UINT message, WPARAM wParam, case WM_KEYDOWN: { RedKey key = translate_key(wParam, HIWORD(lParam) & 0xff, (lParam & (1 << 24)) != 0); window->get_listener().on_key_press(key); + + BYTE key_state[256]; + WCHAR buff[10]; + uint16_t* str_buf = (uint16_t*)buff; + GetKeyboardState(key_state); + int n = ToUnicode(wParam, HIWORD(lParam) & 0xff, key_state, buff, 10, 0); + if (n > 0) { + uint32_t utf32; + while ((utf32 = utf16_to_utf32(str_buf, n)) != 0) { + window->get_listener().on_char(utf32); + } + } + // Allow Windows to translate Alt-F4 to WM_CLOSE message. if (!window->_key_interception_on) { return DefWindowProc(hWnd, message, wParam, lParam); diff --git a/client/x11/platform.cpp b/client/x11/platform.cpp index 217664a4..1ac45ef8 100644 --- a/client/x11/platform.cpp +++ b/client/x11/platform.cpp @@ -64,7 +64,9 @@ static Display* x_display = NULL; static XVisualInfo **vinfo = NULL; -static GLXFBConfig **fb_config; +static GLXFBConfig **fb_config = NULL; +static XIM x_input_method = NULL; +static XIC x_input_context = NULL; static XContext win_proc_context; static ProcessLoop* main_loop = NULL; @@ -193,6 +195,11 @@ GLXFBConfig** XPlatform::get_fbconfig() return fb_config; } +XIC XPlatform::get_input_context() +{ + return x_input_context; +} + void XPlatform::set_win_proc(Window win, win_proc_t proc) { if (XSaveContext(x_display, win, win_proc_context, (XPointer)proc)) { @@ -2021,6 +2028,25 @@ static void init_kbd() num_lock_mask = get_modifier_mask(XK_Num_Lock); } +static void init_XIM() +{ + char app_name[20]; + strcpy(app_name, "spicec"); + + XSetLocaleModifiers(""); + x_input_method = XOpenIM(x_display, NULL, app_name, app_name); + + if (!x_input_method) { + THROW("open IM failed"); + } + + x_input_context = XCreateIC(x_input_method, XNInputStyle, XIMPreeditNone | XIMStatusNone, NULL); + + if (!x_input_context) { + THROW("create IC failed"); + } +} + static int x_error_handler(Display* display, XErrorEvent* error_event) { char error_str[256]; @@ -2072,6 +2098,8 @@ void Platform::init() DBG(0, ""); + setlocale(LC_ALL, ""); + threads_enable = XInitThreads(); @@ -2127,6 +2155,7 @@ void Platform::init() init_kbd(); init_xrandr(); init_xrender(); + init_XIM(); struct sigaction act; memset(&act, 0, sizeof(act)); diff --git a/client/x11/red_window.cpp b/client/x11/red_window.cpp index 1d989b7c..2f17cb1d 100644 --- a/client/x11/red_window.cpp +++ b/client/x11/red_window.cpp @@ -54,6 +54,7 @@ static Display* x_display = NULL; static XContext user_data_context; static bool using_evdev = false; +static XIC x_input_context = NULL; static Atom wm_protocol_atom; static Atom wm_delete_window_atom; @@ -711,6 +712,49 @@ static inline RedButton to_red_button(unsigned int botton, unsigned int& state, return ret; } +void RedWindow_p::handle_key_press_event(RedWindow& window, XKeyEvent* event) +{ + static int buf_size = 0; + static char* utf8_buf = NULL; + static wchar_t* utf32_buf = NULL; + + KeySym key_sym; + Status status; + int len; + + window.get_listener().on_key_press(to_red_key_code(event->keycode)); + for (;;) { + len = XwcLookupString(x_input_context, event, utf32_buf, buf_size, &key_sym, &status); + if (status != XBufferOverflow) { + break; + } + + free(utf32_buf); + free(utf32_buf); + utf8_buf = new char[len]; + utf32_buf = new wchar_t[len]; + buf_size = len; + } + + switch (status) { + case XLookupChars: + case XLookupBoth: { + uint32_t* now = (uint32_t*)utf32_buf; + uint32_t* end = now + len; + + for (; now < end; now++) { + window.get_listener().on_char(*now); + } + break; + } + case XLookupNone: + case XLookupKeySym: + break; + default: + THROW("unexpected status %d", status); + } +} + void RedWindow_p::win_proc(XEvent& event) { XPointer window_pointer; @@ -733,7 +777,7 @@ void RedWindow_p::win_proc(XEvent& event) break; } case KeyPress: - red_window->get_listener().on_key_press(to_red_key_code(event.xkey.keycode)); + red_window->handle_key_press_event(*red_window, &event.xkey); break; case KeyRelease: { RedKey key = to_red_key_code(event.xkey.keycode); @@ -1925,6 +1969,7 @@ void RedWindow::on_focus_in() return; } _focused = true; + XwcResetIC(x_input_context); XPlatform::on_focus_in(); get_listener().on_activate(); if (_trace_key_interception && _pointer_in_window) { @@ -1976,6 +2021,7 @@ void RedWindow::set_menu(Menu* menu) void RedWindow::init() { x_display = XPlatform::get_display(); + x_input_context = XPlatform::get_input_context(); ASSERT(x_display); user_data_context = XUniqueContext(); diff --git a/client/x11/red_window_p.h b/client/x11/red_window_p.h index 8b98b4a6..77933f93 100644 --- a/client/x11/red_window_p.h +++ b/client/x11/red_window_p.h @@ -50,6 +50,7 @@ public: static Cursor create_invisible_cursor(Window window); void set_glx(int width, int height); + static void handle_key_press_event(RedWindow& red_window, XKeyEvent* event); protected: int _screen; diff --git a/client/x11/x_platform.h b/client/x11/x_platform.h index bc2f691f..3d3fd8f9 100644 --- a/client/x11/x_platform.h +++ b/client/x11/x_platform.h @@ -23,6 +23,7 @@ public: static Display* get_display(); static XVisualInfo** get_vinfo(); static GLXFBConfig** get_fbconfig(); + static XIC get_input_context(); typedef void (*win_proc_t)(XEvent& event); static void set_win_proc(Window win, win_proc_t proc); |