From 1e6360843eda6f246aa721c4b6fc3a8c5722fe3a Mon Sep 17 00:00:00 2001 From: Jinhao Date: Mon, 16 Nov 2015 00:38:03 +0800 Subject: [PATCH] fix input issues on Linux(#87) --- source/detail/linux_X11/platform_spec.cpp | 14 +- source/gui/detail/linux_X11/bedrock.cpp | 192 +++++++++++-------- source/gui/widgets/skeletons/text_editor.cpp | 1 + 3 files changed, 129 insertions(+), 78 deletions(-) diff --git a/source/detail/linux_X11/platform_spec.cpp b/source/detail/linux_X11/platform_spec.cpp index 29a9411e..d2244430 100644 --- a/source/detail/linux_X11/platform_spec.cpp +++ b/source/detail/linux_X11/platform_spec.cpp @@ -733,6 +733,8 @@ namespace detail caret_tag * & addr = caret_holder_.carets[wd]; if(0 == addr) { + ::XSetLocaleModifiers(""); + addr = new caret_tag(wd); is_start_routine = (caret_holder_.carets.size() == 1); addr->input_method = ::XOpenIM(display_, 0, 0, 0); @@ -783,8 +785,10 @@ namespace detail ::XFree(attr); } else - addr->input_context = ::XCreateIC(addr->input_method, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing), - XNClientWindow, reinterpret_cast(wd), nullptr); + addr->input_context = ::XCreateIC(addr->input_method, + XNInputStyle, (XIMPreeditNothing | XIMStatusNothing), + XNClientWindow, reinterpret_cast(wd), + XNFocusWindow, reinterpret_cast(wd), nullptr); if(addr->input_context) { @@ -795,6 +799,8 @@ namespace detail XSetWindowAttributes new_attr; new_attr.event_mask = (attr.your_event_mask | addr->input_context_event_mask); ::XChangeWindowAttributes(display_, reinterpret_cast(wd), CWEventMask, &new_attr); + + ::XSetICValues(addr->input_context, XNResetState, XIMPreserveState, nullptr); } ::XFree(preedit_attr); ::XFree(status_attr); @@ -1254,11 +1260,13 @@ namespace detail respond.xclient.data.l[2] = self.atombase_.xdnd_action_copy; } ::XSendEvent(self.display_, self.xdnd_.wd_src, False, NoEventMask, &respond); + ::XFlush(self.display_); if(msg.u.mouse_drop.window) return 1; //Use the packet directly. } } + ::XFlush(self.display_); return 2; } else if(SelectionRequest == evt.type) @@ -1277,7 +1285,7 @@ namespace detail } ::XChangeProperty(self.display_, evt.xselectionrequest.requestor, - evt.xselectionrequest.property, XA_ATOM, sizeof(Atom) * 8, 0, + evt.xselectionrequest.property, XA_ATOM, 32, 0, reinterpret_cast(atoms.size() ? &atoms[0] : 0), static_cast(atoms.size())); } else if(XA_STRING == evt.xselectionrequest.target || self.atombase_.utf8_string == evt.xselectionrequest.target) diff --git a/source/gui/detail/linux_X11/bedrock.cpp b/source/gui/detail/linux_X11/bedrock.cpp index fa51348b..dd595282 100644 --- a/source/gui/detail/linux_X11/bedrock.cpp +++ b/source/gui/detail/linux_X11/bedrock.cpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace nana { @@ -54,10 +55,10 @@ namespace detail int window_count{0}; //The number of windows core_window_t* event_window{nullptr}; bool is_alt_pressed{false}; + bool is_ctrl_pressed{false}; struct platform_detail_tag { - nana::char_t keychar; native_window_type motion_window; nana::point motion_pointer_pos; }platform; @@ -568,6 +569,73 @@ namespace detail if(thrd) thrd->event_window = pre_wd; } + wchar_t os_code_from_keysym(KeySym keysym) + { + switch(keysym) + { + case XK_Alt_L: case XK_Alt_R: + keysym = keyboard::alt; break; + case XK_BackSpace: + keysym = keyboard::backspace; break; + case XK_Tab: + keysym = keyboard::tab; break; + case XK_Escape: + keysym = keyboard::escape; break; + case XK_Return: + keysym = keyboard::enter; break; + case XK_Cancel: + keysym = keyboard::end_of_text; break; //Ctrl+C + case XK_Page_Up: + keysym = keyboard::os_pageup; break; + case XK_Page_Down: + keysym = keyboard::os_pagedown; break; + case XK_Left: case XK_Up: case XK_Right: case XK_Down: + keysym = keyboard::os_arrow_left + (keysym - XK_Left); break; + case XK_Insert: + keysym = keyboard::os_insert; break; + case XK_Delete: + keysym = keyboard::os_del; break; + case XK_Shift_L: case XK_Shift_R: //shift + keysym = 0x10; break; + case XK_Control_L: case XK_Control_R: //ctrl + keysym = 0x11; break; + default: + do + { + //Transfer the keysym for key_press and key_release event to the same behavior with Windows + if('a' <= keysym && keysym <= 'z') + { + keysym = keysym - 'a' + 'A'; + break; + } + //reverts the key + static const char shift_num[]=")!@#$%^&*("; + auto p = std::find(shift_num, shift_num + sizeof shift_num, char(keysym)); + if(p != shift_num + sizeof shift_num) + { + keysym = (p - shift_num + '0'); + break; + } + + switch(keysym) + { + case '~': keysym = '`'; break; + case '_': keysym = '-'; break; + case '+': keysym = '='; break; + case '{': keysym = '['; break; + case '}': keysym = ']'; break; + case '|': keysym = '\\'; break; + case ':': keysym = ';'; break; + case '"': keysym = '\''; break; + case '<': keysym = ','; break; + case '>': keysym = '.'; break; + case '?': keysym = '/'; break; + } + }while(false); + } + return wchar_t(keysym); + } + void window_proc_for_xevent(Display* display, XEvent& xevent) { typedef detail::bedrock::core_window_t core_window_t; @@ -953,40 +1021,13 @@ namespace detail } keybuf[len] = 0; - nana::char_t keychar = 0; + ::nana::char_t os_code = 0; switch(status) { case XLookupKeySym: case XLookupBoth: - switch(keysym) - { - case XK_Alt_L: case XK_Alt_R: - keychar = keyboard::alt; break; - case XK_BackSpace: - keychar = keyboard::backspace; break; - case XK_Tab: - keychar = keyboard::tab; break; - case XK_Escape: - keychar = keyboard::escape; break; - case XK_Return: - keychar = keyboard::enter; break; - case XK_Cancel: - keychar = keyboard::end_of_text;break; //Ctrl+C - case XK_Page_Up: - keychar = keyboard::os_pageup; break; - case XK_Page_Down: - keychar = keyboard::os_pagedown; break; - case XK_Left: case XK_Up: case XK_Right: case XK_Down: - keychar = keyboard::os_arrow_left + (keysym - XK_Left); break; - case XK_Insert: - keychar = keyboard::os_insert; break; - case XK_Delete: - keychar = keyboard::os_del; break; - default: - keychar = keysym; - } - context.platform.keychar = keychar; - if(keychar == keyboard::tab && (false == (msgwnd->flags.tab & detail::tab_type::eating))) //Tab + os_code = os_code_from_keysym(keysym); + if(os_code == keyboard::tab && (false == (msgwnd->flags.tab & detail::tab_type::eating))) //Tab { arg_keyboard argkey; brock.get_key_state(argkey); @@ -997,7 +1038,7 @@ namespace detail brock.wd_manager().do_lazy_refresh(tstop_wd, true); } } - else if(keyboard::alt == keychar) + else if(keyboard::alt == os_code) { context.is_alt_pressed = true; if (brock.whether_keyboard_shortkey() == false) @@ -1010,8 +1051,9 @@ namespace detail arg.evt_code = event_code::key_press; arg.window_handle = reinterpret_cast(msgwnd); arg.ignore = false; - arg.key = static_cast(keychar); + arg.key = os_code; brock.get_key_state(arg); + brock.emit(event_code::key_press, msgwnd, arg, true, &context); msgwnd->root_widget->flags.ignore_menubar_focus = (focused && (brock.focus() != msgwnd)); @@ -1022,25 +1064,19 @@ namespace detail } else { + if(0x11 == os_code) + context.is_ctrl_pressed = true; + arg_keyboard arg; arg.ignore = false; - arg.key = keychar ? keychar : xevent.xkey.keycode; + arg.key = os_code; arg.evt_code = event_code::key_press; brock.get_key_state(arg); arg.window_handle = reinterpret_cast(msgwnd); + brock.emit(event_code::key_press, msgwnd, arg, true, &context); - if((XLookupKeySym == status) && (brock.wd_manager().available(msgwnd))) - { - //call key_char event if status is XLookupKeySym to avaid calling key_char - //twice, because the status would be equal to XLookupChars if the input method is - //enabled for the window. - arg.ignore = false; - arg.evt_code = event_code::key_char; - brock.emit(event_code::key_char, msgwnd, arg, true, & context); - } - - if(msgwnd->root_widget->other.attribute.root->menubar == msgwnd) + if(brock.wd_manager().available(msgwnd) && (msgwnd->root_widget->other.attribute.root->menubar == msgwnd)) { int cmd = (menu_wd && (keyboard::escape == static_cast(arg.key)) ? 1 : 0 ); brock.delay_restore(cmd); @@ -1090,7 +1126,6 @@ namespace detail arg.evt_code = event_code::key_char; arg.window_handle = reinterpret_cast(msgwnd); brock.get_key_state(arg); - msgwnd->together.events_ptr->key_char.emit(arg); if(arg.ignore == false && brock.wd_manager().available(msgwnd)) brock.emit_drawer(event_code::key_char, msgwnd, arg, &context); @@ -1101,6 +1136,7 @@ namespace detail } break; } + brock.wd_manager().do_lazy_refresh(msgwnd, false); if(keybuf != fixbuf) delete [] keybuf; @@ -1109,47 +1145,53 @@ namespace detail break; case KeyRelease: nana::detail::platform_spec::instance().write_keystate(xevent.xkey); - if(context.platform.keychar != keyboard::alt) //Must NOT be an ALT { - msgwnd = brock.focus(); - if(msgwnd) + auto os_code = os_code_from_keysym(::XLookupKeysym(&xevent.xkey, 0)); + if(keyboard::alt != os_code) { - arg_keyboard arg; - arg.evt_code = event_code::key_release; - arg.window_handle = reinterpret_cast(msgwnd); - arg.ignore = false; - arg.key = static_cast(context.platform.keychar); - brock.get_key_state(arg); - brock.emit(event_code::key_release, msgwnd, arg, true, &context); - } + if(0x11 == os_code) + context.is_ctrl_pressed = false; - if (context.platform.keychar < keyboard::os_arrow_left || keyboard::os_arrow_down < context.platform.keychar) - brock.delay_restore(2); //Restores while key release - } - else - { - context.is_alt_pressed = false; - if (brock.set_keyboard_shortkey(false) == false) - { - msgwnd = msgwnd->root_widget->other.attribute.root->menubar; - if (msgwnd) + msgwnd = brock.focus(); + if(msgwnd) { - bool set_focus = (brock.focus() != msgwnd) && (!msgwnd->root_widget->flags.ignore_menubar_focus); - if (set_focus) - brock.wd_manager().set_focus(msgwnd, false); - arg_keyboard arg; arg.evt_code = event_code::key_release; arg.window_handle = reinterpret_cast(msgwnd); arg.ignore = false; - arg.key = static_cast(context.platform.keychar); + arg.key = os_code; brock.get_key_state(arg); brock.emit(event_code::key_release, msgwnd, arg, true, &context); + } - if (!set_focus) + if(os_code < keyboard::os_arrow_left || keyboard::os_arrow_down < os_code) + brock.delay_restore(2); //Restores while key release + } + else + { + context.is_alt_pressed = false; + if (brock.set_keyboard_shortkey(false) == false) + { + msgwnd = msgwnd->root_widget->other.attribute.root->menubar; + if (msgwnd) { - brock.set_menubar_taken(nullptr); - msgwnd->root_widget->flags.ignore_menubar_focus = false; + bool set_focus = (brock.focus() != msgwnd) && (!msgwnd->root_widget->flags.ignore_menubar_focus); + if (set_focus) + brock.wd_manager().set_focus(msgwnd, false); + + arg_keyboard arg; + arg.evt_code = event_code::key_release; + arg.window_handle = reinterpret_cast(msgwnd); + arg.ignore = false; + arg.key = os_code; + brock.get_key_state(arg); + brock.emit(event_code::key_release, msgwnd, arg, true, &context); + + if (!set_focus) + { + brock.set_menubar_taken(nullptr); + msgwnd->root_widget->flags.ignore_menubar_focus = false; + } } } } diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 461a734e..ff044b4d 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -2716,6 +2716,7 @@ namespace nana{ namespace widgets auto length = text.size(); textbase_.insert(crtpos, std::move(text)); + crtpos.x += static_cast(length); behavior_->pre_calc_line(crtpos.y, width_pixels()); }