summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/application.cpp7
-rw-r--r--client/application.h1
-rw-r--r--client/inputs_handler.h1
-rw-r--r--client/red_window.h1
-rw-r--r--client/screen.cpp5
-rw-r--r--client/screen.h1
-rw-r--r--client/windows/red_window.cpp53
-rw-r--r--client/x11/platform.cpp31
-rw-r--r--client/x11/red_window.cpp48
-rw-r--r--client/x11/red_window_p.h1
-rw-r--r--client/x11/x_platform.h1
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);