Nux

Merge lp:~brandontschaefer/nux/xim-preedit-support into lp:nux

Proposed by Brandon Schaefer
Status: Merged
Approved by: Christopher Townsend
Approved revision: 840
Merged at revision: 833
Proposed branch: lp:~brandontschaefer/nux/xim-preedit-support
Merge into: lp:nux
Diff against target: 1203 lines (+584/-103)
17 files modified
Nux/MainLoopGLib.cpp (+7/-4)
Nux/Makefile.am (+8/-3)
Nux/TextEntry.cpp (+31/-2)
Nux/TextEntry.h (+5/-0)
Nux/WindowThread.cpp (+37/-1)
Nux/WindowThread.h (+15/-0)
Nux/XICClient.cpp (+226/-20)
Nux/XICClient.h (+25/-3)
Nux/XIMCallbacks.cpp (+189/-0)
Nux/XIMController.cpp (+14/-5)
Nux/XIMController.h (+3/-1)
NuxGraphics/GraphicsDisplayX11.cpp (+10/-45)
NuxGraphics/GraphicsDisplayX11.h (+2/-6)
NuxGraphics/Makefile.am (+2/-6)
NuxGraphics/XInputWindow.cpp (+0/-5)
configure.ac (+2/-2)
debian/changelog (+8/-0)
To merge this branch: bzr merge lp:~brandontschaefer/nux/xim-preedit-support
Reviewer Review Type Date Requested Status
Christopher Townsend Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+199569@code.launchpad.net

Commit message

Adds preedit support to XIM. Such as preedit rendering in the TextEntry.

Move XIM support from GraphicsDisplayX11 to WindowThread.

Description of the change

Add preedit support to XIM.

Had to move XIM support from GraphicsDisplayX11 to WindowThread

This should land when this branch lands:
https://code.launchpad.net/~brandontschaefer/unity/libnux-bump/+merge/200334

To post a comment you must log in.
834. By Brandon Schaefer

* Don't need that header

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:834
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~brandontschaefer/nux/xim-preedit-support/+merge/199569/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/nux-ci/97/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/nux-trusty-amd64-ci/19/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/nux-trusty-armhf-ci/19
    FAILURE: http://jenkins.qa.ubuntu.com/job/nux-trusty-i386-ci/19/console

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/nux-ci/97/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Christopher Townsend (townsend) wrote :

Couple of things right off the bat.

1. Need a commit message :)
2. Is there a bug associated with this work?
3. How do you test this? What is the old behavior and what am I expecting with this change?

Thanks!

review: Needs Information
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Hey Chris,

So there are some AP tests in unity. The old bevaior should be the same, ie. type in the an IM and text gets commited. Whats changed is they style, now preedit gets rendered to the TextEntry while the user types in they IM. Some IMs such as fctix doesn't support it by default (for some reason). So to test it out you'll need to go into:

$HOME/.config/fcitx/conf/fcitx-xim.config

Then set:
UseOnTheSpotStyle=True

835. By Brandon Schaefer

[ Brandon Schaefer ]
Add XIM preedit support

Update the deb change log

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Christopher Townsend (townsend) wrote :

If $XMODIFIERS is blank, I get a seg fault crash. I don't think is default, but I'm sure it will happen.

Also,

839 +//#include "Nux.h"
840 +//#include "TextEntry.h"
841 +

needs to be removed since it's commented out.

review: Needs Fixing
836. By Brandon Schaefer

* Fix a crash when XMOD was blank. We need to make sure we are passing in a NULL
  name if we don't have any callbacks!

Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Fixed issue, I was passing in XNPreeditAttributes and XNStatusAttributes to XCreateIC even when p_list and s_list was NULL. If they were NULL I can't pass in XNPreeditAttributes, instead it should also pass in a NULL name, so it doesn't try to access a NULL list.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Christopher Townsend (townsend) wrote :

I think the Nux version should be bumped to 4.0.5. As I mentioned in https://code.launchpad.net/~brandontschaefer/unity/libnux-bump/+merge/200334/comments/465544, the Nux version built in the daily-ppa is already greater than the version you have proposed here.

review: Needs Fixing
837. By Brandon Schaefer

* Bump to 4.0.5

838. By Brandon Schaefer

[ Brandon Schaefer ]
* Add XIM preedit support
* Bump version to 4.0.5

Update changelog

839. By Brandon Schaefer

* Add missing space

840. By Brandon Schaefer

* Back to 1

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Christopher Townsend (townsend) wrote :

Great, this all looks good now!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Nux/MainLoopGLib.cpp'
2--- Nux/MainLoopGLib.cpp 2013-11-06 13:56:07 +0000
3+++ Nux/MainLoopGLib.cpp 2014-01-03 16:37:28 +0000
4@@ -43,6 +43,11 @@
5 unsigned int id;
6 } TimeoutData;
7
8+ Event GetSystemEvent(WindowThread* window_thread)
9+ {
10+ return window_thread->GetNextEvent();
11+ }
12+
13 gboolean nux_timeout_dispatch(gpointer user_data)
14 {
15 bool repeat = false;
16@@ -59,8 +64,7 @@
17 }
18 else
19 {
20- Event event;
21- dd->window_thread->GetGraphicsDisplay().GetSystemEvent(&event);
22+ Event event = GetSystemEvent(dd->window_thread);
23 return_code = dd->window_thread->ProcessEvent(event);
24 }
25
26@@ -128,8 +132,7 @@
27 nux_glib_threads_lock();
28 WindowThread *window_thread = NUX_STATIC_CAST(WindowThread *, user_data);
29
30- Event event;
31- window_thread->GetGraphicsDisplay().GetSystemEvent(&event);
32+ Event event = GetSystemEvent(window_thread);
33 unsigned int return_code = window_thread->ProcessEvent(event);
34
35 if (return_code == 0 && !window_thread->IsEmbeddedWindow())
36
37=== modified file 'Nux/Makefile.am'
38--- Nux/Makefile.am 2013-04-18 13:28:09 +0000
39+++ Nux/Makefile.am 2014-01-03 16:37:28 +0000
40@@ -129,7 +129,10 @@
41
42 if USE_X11
43 source_cpp += \
44- $(srcdir)/InputMethodIBus.cpp
45+ $(srcdir)/InputMethodIBus.cpp \
46+ $(srcdir)/XICClient.cpp \
47+ $(srcdir)/XIMCallbacks.cpp \
48+ $(srcdir)/XIMController.cpp
49 endif
50
51 if HAVE_GEIS
52@@ -243,8 +246,10 @@
53 endif
54
55 if USE_X11
56-source_cpp += \
57- $(srcdir)/InputMethodIBus.h
58+source_h += \
59+ $(srcdir)/InputMethodIBus.h \
60+ $(srcdir)/XICClient.h \
61+ $(srcdir)/XIMController.h
62 endif
63
64 if HAVE_GEIS
65
66=== modified file 'Nux/TextEntry.cpp'
67--- Nux/TextEntry.cpp 2013-11-04 14:59:20 +0000
68+++ Nux/TextEntry.cpp 2014-01-03 16:37:28 +0000
69@@ -884,7 +884,7 @@
70 need_im_reset_ = true;
71 #if defined(USE_X11)
72 ime_->Focus();
73- nux::GetWindowThread()->GetGraphicsDisplay().XICFocus();
74+ nux::GetWindowThread()->XICFocus(this);
75 #endif
76 //gtk_im_context_focus_in(im_context_);
77 //UpdateIMCursorLocation();
78@@ -907,7 +907,7 @@
79 need_im_reset_ = true;
80 #if defined(USE_X11)
81 ime_->Blur();
82- nux::GetWindowThread()->GetGraphicsDisplay().XICUnFocus();
83+ nux::GetWindowThread()->XICUnFocus();
84 #endif
85 //gtk_im_context_focus_out(im_context_);
86 }
87@@ -1033,6 +1033,35 @@
88 }
89 }
90
91+ void TextEntry::PreeditStarted()
92+ {
93+ ime_active_ = true;
94+ }
95+
96+ void TextEntry::UpdatePreedit(std::string const& preedit, int cursor)
97+ {
98+ preedit_ = preedit;
99+ preedit_cursor_ = cursor;
100+ QueueRefresh(true, true);
101+ }
102+
103+ void TextEntry::UpdatePreeditAttribs(PangoAttrList* list)
104+ {
105+ if (preedit_attrs_)
106+ {
107+ pango_attr_list_unref(preedit_attrs_);
108+ preedit_attrs_ = NULL;
109+ }
110+
111+ preedit_attrs_ = list;
112+ }
113+
114+ void TextEntry::ClearPreedit()
115+ {
116+ ResetPreedit();
117+ QueueRefresh(true, true);
118+ }
119+
120 void TextEntry::DrawText(CairoGraphics *canvas)
121 {
122 PangoLayout *layout = EnsureLayout();
123
124=== modified file 'Nux/TextEntry.h'
125--- Nux/TextEntry.h 2013-11-04 14:59:20 +0000
126+++ Nux/TextEntry.h 2014-01-03 16:37:28 +0000
127@@ -227,6 +227,11 @@
128 bool PasswordMode() const;
129 std::string GetPasswordChar();
130
131+ void PreeditStarted();
132+ void UpdatePreedit(std::string const& preedit, int cursor);
133+ void UpdatePreeditAttribs(PangoAttrList* list);
134+ void ClearPreedit();
135+
136 protected:
137 bool _block_focus; // used to selectively ignore focus keyevents
138
139
140=== modified file 'Nux/WindowThread.cpp'
141--- Nux/WindowThread.cpp 2013-11-07 11:53:10 +0000
142+++ Nux/WindowThread.cpp 2014-01-03 16:37:28 +0000
143@@ -118,6 +118,7 @@
144 WindowThread::~WindowThread()
145 {
146 #if (defined(NUX_OS_LINUX) || defined(NUX_USE_GLIB_LOOP_ON_WINDOWS)) && (!defined(NUX_DISABLE_GLIB_LOOP))
147+ xim_controller_.reset();
148 CleanupGlibLoop();
149 #endif
150
151@@ -521,6 +522,24 @@
152
153 extern EventToNameStruct EventToName[];
154
155+ Event WindowThread::GetNextEvent()
156+ {
157+ Event event;
158+ graphics_display_->GetSystemEvent(&event);
159+
160+#if defined(NUX_OS_LINUX) && defined(USE_X11)
161+ // Make sure the current xic is synced up with the current event window
162+ if ((event.type == KeyPress || event.type == KeyRelease) &&
163+ event.x11_window && xim_controller_->GetCurrentWindow() != event.x11_window)
164+ {
165+ xim_controller_->SetFocusedWindow(event.x11_window);
166+ graphics_display_->SetCurrentXIC(xim_controller_->GetXIC());
167+ }
168+#endif
169+
170+ return event;
171+ }
172+
173 #if (!defined(NUX_OS_LINUX) && !defined(NUX_USE_GLIB_LOOP_ON_WINDOWS)) || defined(NUX_DISABLE_GLIB_LOOP)
174 #ifdef NUX_GESTURES_SUPPORT
175 Event *WindowThread::FetchNextEvent()
176@@ -1148,6 +1167,8 @@
177 timer_manager_ = new TimerHandler(this);
178 window_compositor_ = new WindowCompositor(this);
179
180+ xim_controller_ = std::make_shared<XIMController>(graphics_display_->GetX11Display());
181+
182 SetThreadState(THREADRUNNING);
183 thread_ctor_called_ = true;
184 return true;
185@@ -1245,7 +1266,7 @@
186 x11display_ = XOpenDisplay(NULL);
187 ownx11display_ = true;
188 }
189-
190+
191 graphics_display_ = gGLWindowManager.CreateFromForeignWindow(x11display_, X11Window, OpenGLContext);
192
193 if (graphics_display_ == 0)
194@@ -1264,6 +1285,8 @@
195 timer_manager_ = new TimerHandler(this);
196 window_compositor_ = new WindowCompositor(this);
197
198+ xim_controller_ = std::make_shared<XIMController>(graphics_display_->GetX11Display());
199+
200 SetThreadState(THREADRUNNING);
201 thread_ctor_called_ = true;
202
203@@ -1776,6 +1799,19 @@
204 _external_fds.erase (it);
205 }
206 }
207+#if defined(NUX_OS_LINUX) && defined(USE_X11)
208+ void WindowThread::XICFocus(TextEntry* text_entry)
209+ {
210+ xim_controller_->FocusInXIC();
211+ xim_controller_->SetCurrentTextEntry(text_entry);
212+ graphics_display_->SetCurrentXIC(xim_controller_->GetXIC());
213+ }
214+
215+ void WindowThread::XICUnFocus()
216+ {
217+ xim_controller_->FocusOutXIC();
218+ }
219+#endif
220
221 GraphicsDisplay& WindowThread::GetGraphicsDisplay() const
222 {
223
224=== modified file 'Nux/WindowThread.h'
225--- Nux/WindowThread.h 2013-10-30 23:26:54 +0000
226+++ Nux/WindowThread.h 2014-01-03 16:37:28 +0000
227@@ -29,6 +29,10 @@
228 #include "GeisAdapter.h"
229 #endif
230
231+#if defined(NUX_OS_LINUX) && defined(USE_X11)
232+#include "XIMController.h"
233+#endif
234+
235 namespace nux
236 {
237
238@@ -358,6 +362,8 @@
239
240 std::vector<Geometry> GetPresentationListGeometries() const;
241
242+ Event GetNextEvent();
243+
244 #ifdef NUX_GESTURES_SUPPORT
245 /*!
246 Simple wrapper for ProcessEvent for connection with GeisAdapter::event_ready
247@@ -371,6 +377,11 @@
248 void WatchFdForEvents(int fd, const FdWatchCallback &);
249 void UnwatchFd(int fd);
250
251+#if defined(NUX_OS_LINUX) && defined(USE_X11)
252+ void XICFocus(TextEntry* text_entry);
253+ void XICUnFocus();
254+#endif
255+
256 protected:
257
258 /*!
259@@ -709,6 +720,10 @@
260 std::unique_ptr<GeisAdapter> geis_adapter_;
261 #endif
262
263+#if defined(NUX_OS_LINUX) && defined(USE_X11)
264+ std::shared_ptr<XIMController> xim_controller_;
265+#endif
266+
267 /*!
268 Add a timeout and return the timeout index.
269 This function is used internally by Nux.
270
271=== renamed file 'NuxGraphics/XICClient.cpp' => 'Nux/XICClient.cpp'
272--- NuxGraphics/XICClient.cpp 2013-07-18 00:09:46 +0000
273+++ Nux/XICClient.cpp 2014-01-03 16:37:28 +0000
274@@ -1,5 +1,5 @@
275 /*
276-* Copyright 2012 Inalogic® Inc.
277+* Copyright 2012-2013 Inalogic® Inc.
278 *
279 * This program is free software: you can redistribute it and/or modify it
280 * under the terms of the GNU Lesser General Public License, as
281@@ -21,54 +21,260 @@
282
283 #include "XICClient.h"
284
285+#include "NuxCore/Logger.h"
286+
287+DECLARE_LOGGER(logger, "xic.client");
288+
289+using namespace std;
290+
291 namespace nux
292 {
293
294+int const FEEDBACK_MASK = (XIMUnderline | XIMReverse);
295+
296 XICClient::XICClient()
297- : xic_(NULL)
298+ : xic_(nullptr)
299 , xim_style_(0)
300 , focused_(false)
301 {
302 }
303
304-void XICClient::ResetXIC(XIM xim, Window window)
305+void XICClient::ResetXIC(XIM xim, Window window, Display* display)
306 {
307 if (!xim_style_)
308 SetupXIMStyle(xim);
309
310- SetupXIC(xim, window);
311-}
312-
313-void XICClient::SetupXIC(XIM xim, Window window)
314+ SetupXIC(xim, window, display);
315+}
316+
317+void XICClient::SetCurrentTextEntry(TextEntry* text_entry)
318+{
319+ text_entry_ = text_entry;
320+}
321+
322+static int preedit_caret_callback(XIC xic,
323+ XPointer clientdata,
324+ XIMPreeditCaretCallbackStruct* call_data)
325+{
326+ return 0;
327+}
328+
329+static int status_start_callback(XIC xic,
330+ XPointer clientdata,
331+ XIMPreeditDrawCallbackStruct* call_data)
332+{
333+ return 0;
334+}
335+
336+static void status_draw_callback(XIC xic,
337+ XPointer clientdata,
338+ XPointer* call_data)
339+{
340+}
341+
342+static void status_done_callback(XIC xic,
343+ XPointer clientdata,
344+ XPointer* call_data)
345+{
346+}
347+
348+XVaNestedList XICClient::GetPreeditCallbacks()
349+{
350+ preedit_start_cb_.callback = (XIMProc)XICClient::PreeditStartCallback;
351+ preedit_start_cb_.client_data = nullptr;
352+
353+ preedit_done_cb_.callback = (XIMProc)XICClient::PreeditDoneCallback;
354+ preedit_done_cb_.client_data = (XPointer)text_entry_;
355+
356+ preedit_draw_cb_.callback = (XIMProc)XICClient::PreeditDrawCallback;
357+ preedit_draw_cb_.client_data = (XPointer)text_entry_;
358+
359+ preedit_caret_cb_.callback = (XIMProc)preedit_caret_callback;
360+ preedit_caret_cb_.client_data = nullptr;
361+
362+ XVaNestedList p_list = nullptr;
363+ p_list = XVaCreateNestedList(0,
364+ XNPreeditStartCallback, &preedit_start_cb_,
365+ XNPreeditDoneCallback, &preedit_done_cb_,
366+ XNPreeditDrawCallback, &preedit_draw_cb_,
367+ XNPreeditCaretCallback, &preedit_caret_cb_,
368+ nullptr);
369+
370+ return p_list;
371+}
372+
373+XVaNestedList XICClient::GetStatusCallbacks()
374+{
375+ status_start_cb_.callback = (XIMProc)status_start_callback;
376+ status_start_cb_.client_data = nullptr;
377+
378+ status_done_cb_.callback = (XIMProc)status_done_callback;
379+ status_done_cb_.client_data = nullptr;
380+
381+ status_draw_cb_.callback = (XIMProc)status_draw_callback;
382+ status_draw_cb_.client_data = nullptr;
383+
384+ XVaNestedList s_list = nullptr;
385+ s_list = XVaCreateNestedList(0,
386+ XNStatusStartCallback, &status_start_callback,
387+ XNStatusDoneCallback, &status_done_callback,
388+ XNStatusDrawCallback, &status_draw_callback,
389+ nullptr);
390+
391+ return s_list;
392+}
393+
394+XIMStyle XICClient::FilterXIMStyle()
395+{
396+ XIMStyle style = 0;
397+
398+ if (xim_style_ & XIMPreeditCallbacks)
399+ {
400+ style |= XIMPreeditCallbacks;
401+ }
402+ else if (xim_style_ & XIMPreeditNone)
403+ {
404+ style |= XIMPreeditNone;
405+ }
406+ else
407+ {
408+ style |= XIMPreeditNothing;
409+ }
410+
411+ if (xim_style_ & XIMStatusCallbacks)
412+ {
413+ style |= XIMStatusCallbacks;
414+ }
415+ else if (xim_style_ & XIMStatusNone)
416+ {
417+ style |= XIMStatusNone;
418+ }
419+ else
420+ {
421+ style |= XIMStatusNothing;
422+ }
423+
424+ return style;
425+}
426+
427+void XICClient::SetupXIC(XIM xim, Window window, Display* display)
428 {
429 if (xic_)
430 DestroyXIC();
431
432- xic_ = XCreateIC(xim, XNInputStyle, xim_style_, XNClientWindow, window, XNFocusWindow, window, NULL);
433+ XIMStyle style = FilterXIMStyle();
434+
435+ const char* p_name = nullptr;
436+ XVaNestedList p_list = nullptr;
437+ if (style & XIMPreeditCallbacks)
438+ {
439+ p_list = GetPreeditCallbacks();
440+ p_name = XNPreeditAttributes;
441+ }
442+
443+ const char* s_name = nullptr;
444+ XVaNestedList s_list = nullptr;
445+ if (style & XIMStatusCallbacks)
446+ {
447+ s_list = GetStatusCallbacks();
448+ s_name = XNStatusAttributes;
449+ }
450+
451+ xic_ = XCreateIC(xim, XNInputStyle, style,
452+ XNClientWindow, window,
453+ p_name, p_list,
454+ s_name, s_list,
455+ nullptr);
456+
457+ if (p_list)
458+ XFree(p_list);
459+ if (s_list)
460+ XFree(s_list);
461+
462+ xim_style_ = style;
463+}
464+
465+XIMStyle ChooseBetterStyle(XIMStyle style1, XIMStyle style2)
466+{
467+ XIMStyle s,t;
468+ XIMStyle preedit = XIMPreeditArea | XIMPreeditCallbacks |
469+ XIMPreeditPosition | XIMPreeditNothing | XIMPreeditNone;
470+
471+ XIMStyle status = XIMStatusArea | XIMStatusCallbacks |
472+ XIMStatusNothing | XIMStatusNone;
473+
474+ if (style1 == 0)
475+ return style2;
476+
477+ if (style2 == 0)
478+ return style1;
479+
480+ if ((style1 & (preedit | status)) == (style2 & (preedit | status)))
481+ return style1;
482+
483+ s = style1 & preedit;
484+ t = style2 & preedit;
485+ if (s != t)
486+ {
487+ if (s | t | XIMPreeditCallbacks)
488+ return (s == XIMPreeditCallbacks) ? style1 : style2;
489+ else if (s | t | XIMPreeditPosition)
490+ return (s == XIMPreeditPosition) ? style1 : style2;
491+ else if (s | t | XIMPreeditArea)
492+ return (s == XIMPreeditArea) ? style1 : style2;
493+ else if (s | t | XIMPreeditNothing)
494+ return (s == XIMPreeditNothing) ? style1 : style2;
495+ else if (s | t | XIMPreeditNone)
496+ return (s == XIMPreeditNone) ? style1 : style2;
497+ }
498+ else
499+ {
500+ s = style1 & status;
501+ t = style2 & status;
502+
503+ if (s | t | XIMStatusCallbacks)
504+ return (s == XIMStatusCallbacks) ? style1 : style2;
505+ else if (s | t | XIMStatusArea)
506+ return (s == XIMStatusArea) ? style1 : style2;
507+ else if (s | t | XIMStatusNothing)
508+ return (s == XIMStatusNothing) ? style1 : style2;
509+ else if (s | t | XIMStatusNone)
510+ return (s == XIMStatusNone) ? style1 : style2;
511+ }
512 }
513
514 void XICClient::SetupXIMStyle(XIM xim)
515 {
516 int i;
517- XIMStyles *xim_styles = NULL;
518- XIMStyle root_style = (XIMPreeditNothing|XIMStatusNothing);
519-
520- XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL);
521-
522+ XIMStyles *xim_styles = nullptr;
523+ XIMStyle prefered_style = XIMPreeditCallbacks | XIMStatusCallbacks;
524+
525+ XGetIMValues(xim, XNQueryInputStyle, &xim_styles, nullptr);
526+
527+ XIMStyle best = 0, style = 0;
528 for (i = 0; i < xim_styles->count_styles; ++i)
529- if (xim_styles->supported_styles[i] == root_style)
530+ {
531+ style = xim_styles->supported_styles[i];
532+
533+ if ((style & prefered_style) == style)
534+ {
535+ best = ChooseBetterStyle(style, best);
536 break;
537+ }
538+ else
539+ {
540+ best = ChooseBetterStyle(style, best);
541+ }
542+ }
543
544- if (i >= xim_styles->count_styles)
545- xim_style_ = 0;
546- xim_style_ = root_style;
547+ xim_style_ = best;
548
549 XFree(xim_styles);
550 }
551
552 bool XICClient::HasXIC() const
553 {
554- return xic_ != NULL;
555+ return xic_ != nullptr;
556 }
557
558 XIC XICClient::GetXIC() const
559@@ -78,7 +284,7 @@
560
561 void XICClient::Reinitialize()
562 {
563- xic_ = NULL;
564+ xic_ = nullptr;
565 xim_style_ = 0;
566 focused_ = false;
567 }
568@@ -111,7 +317,7 @@
569 if (xic_)
570 {
571 XDestroyIC(xic_);
572- xic_ = NULL;
573+ xic_ = nullptr;
574 }
575 }
576
577
578=== renamed file 'NuxGraphics/XICClient.h' => 'Nux/XICClient.h'
579--- NuxGraphics/XICClient.h 2012-11-27 19:30:57 +0000
580+++ Nux/XICClient.h 2014-01-03 16:37:28 +0000
581@@ -1,5 +1,5 @@
582 /*
583-* Copyright 2012 Inalogic® Inc.
584+* Copyright 2012-2013 Inalogic® Inc.
585 *
586 * This program is free software: you can redistribute it and/or modify it
587 * under the terms of the GNU Lesser General Public License, as
588@@ -27,13 +27,15 @@
589
590 namespace nux
591 {
592+ class TextEntry;
593
594 class XICClient
595 {
596 public:
597 XICClient();
598
599- void ResetXIC(XIM xim, Window window);
600+ void ResetXIC(XIM xim, Window window, Display* display);
601+ void SetCurrentTextEntry(TextEntry* text_entry_);
602
603 bool HasXIC() const;
604 XIC GetXIC() const;
605@@ -46,12 +48,32 @@
606
607 void DestroyXIC();
608 private:
609- void SetupXIC(XIM xim, Window window);
610+ void SetupXIC(XIM xim, Window window, Display* display);
611 void SetupXIMStyle(XIM xim);
612
613+ XVaNestedList GetPreeditCallbacks();
614+ XVaNestedList GetStatusCallbacks();
615+ XIMStyle FilterXIMStyle();
616+
617+ static int PreeditStartCallback(XIC xic, XPointer clientdata, XPointer data);
618+ static int PreeditDoneCallback(XIC xic, XPointer clientdata, XPointer data);
619+
620+ static int PreeditDrawCallback(XIC xic, XPointer clientdata,
621+ XIMPreeditDrawCallbackStruct* call_data);
622+
623+ TextEntry* text_entry_;
624 XIC xic_;
625 XIMStyle xim_style_;
626
627+ XIMCallback preedit_start_cb_;
628+ XIMCallback preedit_done_cb_;
629+ XIMCallback preedit_draw_cb_;
630+ XIMCallback preedit_caret_cb_;
631+
632+ XIMCallback status_start_cb_;
633+ XIMCallback status_done_cb_;
634+ XIMCallback status_draw_cb_;
635+
636 bool focused_;
637 };
638
639
640=== added file 'Nux/XIMCallbacks.cpp'
641--- Nux/XIMCallbacks.cpp 1970-01-01 00:00:00 +0000
642+++ Nux/XIMCallbacks.cpp 2014-01-03 16:37:28 +0000
643@@ -0,0 +1,189 @@
644+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
645+/*
646+ * Copyright 2013 Inalogic® Inc.
647+ *
648+ * This program is free software: you can redistribute it and/or modify it
649+ * under the terms of the GNU Lesser General Public License, as
650+ * published by the Free Software Foundation; either version 2.1 or 3.0
651+ * of the License.
652+ *
653+ * This program is distributed in the hope that it will be useful, but
654+ * WITHOUT ANY WARRANTY; without even the implied warranties of
655+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
656+ * PURPOSE. See the applicable version of the GNU Lesser General Public
657+ * License for more details.
658+ *
659+ * You should have received a copy of both the GNU Lesser General Public
660+ * License along with this program. If not, see <http://www.gnu.org/licenses/>
661+ *
662+ * Authored by: Brandon Schaefer
663+ *
664+ */
665+
666+
667+#include "Nux.h"
668+#include "TextEntry.h"
669+#include "XICClient.h"
670+
671+#include <string>
672+
673+#include "NuxCore/Logger.h"
674+
675+DECLARE_LOGGER(logger, "xim.callbacks");
676+
677+using namespace std;
678+
679+namespace nux
680+{
681+
682+int const FEEDBACK_MASK = (XIMUnderline | XIMReverse);
683+
684+int XICClient::PreeditStartCallback(XIC xic,
685+ XPointer clientdata,
686+ XPointer call_data)
687+{
688+ TextEntry* text_entry = (TextEntry*)clientdata;
689+ if (text_entry)
690+ {
691+ text_entry->PreeditStarted();
692+ }
693+
694+ return 0;
695+}
696+
697+int XICClient::PreeditDoneCallback(XIC xic,
698+ XPointer clientdata,
699+ XPointer call_data)
700+{
701+ TextEntry* text_entry = (TextEntry*)clientdata;
702+ if (text_entry)
703+ {
704+ text_entry->ClearPreedit();
705+ }
706+
707+ return 0;
708+}
709+
710+void add_feedback_attr(PangoAttrList* attrs,
711+ char const* str,
712+ XIMFeedback feedback,
713+ int start,
714+ int end)
715+{
716+ PangoAttribute* attr;
717+
718+ int start_index = g_utf8_offset_to_pointer(str, start) - str;
719+ int end_index = g_utf8_offset_to_pointer(str, end) - str;
720+
721+ if (feedback & XIMUnderline)
722+ {
723+ attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
724+ attr->start_index = start_index;
725+ attr->end_index = end_index;
726+
727+ pango_attr_list_insert (attrs, attr);
728+ }
729+
730+ if (feedback & XIMReverse)
731+ {
732+ attr = pango_attr_foreground_new(0xFFFF, 0xFFFF, 0xFFFF);
733+ attr->start_index = start_index;
734+ attr->end_index = end_index;
735+
736+ pango_attr_list_insert (attrs, attr);
737+
738+ attr = pango_attr_background_new(0x0, 0x0, 0x0);
739+
740+ attr->start_index = start_index;
741+ attr->end_index = end_index;
742+
743+ pango_attr_list_insert (attrs, attr);
744+ }
745+
746+ if (feedback & ~FEEDBACK_MASK)
747+ {
748+ LOG_WARN(logger) << "Unrenderer feedback: " << (feedback & ~FEEDBACK_MASK);
749+ }
750+}
751+
752+void feedback_to_pango_list(PangoAttrList** attrs,
753+ char const* str,
754+ int nfeedbacks,
755+ XIMFeedback* feedbacks)
756+{
757+ XIMFeedback last_feedback = 0;
758+ int start = -1;
759+ int i = 0;
760+
761+ if (attrs)
762+ {
763+ *attrs = pango_attr_list_new();
764+
765+ for (i = 0; i < nfeedbacks; ++i)
766+ {
767+ XIMFeedback new_feedback = (feedbacks[i] & FEEDBACK_MASK);
768+ if (new_feedback != last_feedback)
769+ {
770+ last_feedback = new_feedback;
771+ start = i;
772+ }
773+ else
774+ {
775+ PangoAttribute* attr;
776+
777+ int start_index = g_utf8_offset_to_pointer(str, start) - str;
778+ int end_index = g_utf8_offset_to_pointer(str, i) - str;
779+
780+ attr = pango_attr_foreground_new(0x0, 0x0, 0x0);
781+ attr->start_index = start_index;
782+ attr->end_index = end_index;
783+
784+ pango_attr_list_insert (*attrs, attr);
785+
786+ attr = pango_attr_background_new(0xFFFF, 0xFFFF, 0xFFFF);
787+
788+ attr->start_index = start_index;
789+ attr->end_index = end_index;
790+
791+ pango_attr_list_insert (*attrs, attr);
792+ }
793+ }
794+
795+ if (start >= 0)
796+ add_feedback_attr(*attrs, str, last_feedback, start, i);
797+ }
798+}
799+
800+int XICClient::PreeditDrawCallback(XIC xic,
801+ XPointer clientdata,
802+ XIMPreeditDrawCallbackStruct* call_data)
803+{
804+ TextEntry* text_entry = (TextEntry*)clientdata;
805+
806+ if (call_data->text && text_entry)
807+ {
808+ string preedit;
809+
810+ // TODO Actually handle this correctly...
811+ if (call_data->text->encoding_is_wchar)
812+ {
813+ preedit = string("");
814+ }
815+ else
816+ {
817+ preedit = string(call_data->text->string.multi_byte);
818+ }
819+
820+ PangoAttrList* preedit_attr;
821+ feedback_to_pango_list(&preedit_attr, preedit.c_str(),
822+ call_data->text->length,
823+ call_data->text->feedback);
824+
825+ text_entry->UpdatePreeditAttribs(preedit_attr);
826+ text_entry->UpdatePreedit(preedit, call_data->caret);
827+ }
828+
829+ return 0;
830+}
831+
832+}
833
834=== renamed file 'NuxGraphics/XIMController.cpp' => 'Nux/XIMController.cpp'
835--- NuxGraphics/XIMController.cpp 2013-10-10 23:36:28 +0000
836+++ Nux/XIMController.cpp 2014-01-03 16:37:28 +0000
837@@ -1,5 +1,5 @@
838 /*
839-* Copyright 2012 Inalogic® Inc.
840+* Copyright 2012-2013 Inalogic® Inc.
841 *
842 * This program is free software: you can redistribute it and/or modify it
843 * under the terms of the GNU Lesser General Public License, as
844@@ -32,7 +32,7 @@
845 XIMController::XIMController(Display* display)
846 : display_(display)
847 , window_(0)
848- , xim_(NULL)
849+ , xim_(nullptr)
850 {
851 InitXIMCallback();
852 }
853@@ -51,7 +51,15 @@
854 window_ = window;
855
856 if (xim_)
857- xic_client_.ResetXIC(xim_, window);
858+ xic_client_.ResetXIC(xim_, window, display_);
859+}
860+
861+void XIMController::SetCurrentTextEntry(TextEntry* text_entry)
862+{
863+ xic_client_.SetCurrentTextEntry(text_entry);
864+
865+ if (xim_)
866+ xic_client_.ResetXIC(xim_, window_, display_);
867 }
868
869 void XIMController::RemoveFocusedWindow()
870@@ -78,7 +86,7 @@
871 void XIMController::FocusOutXIC()
872 {
873 xic_client_.FocusOutXIC();
874- xic_client_.DestroyXIC();
875+ xic_client_.SetCurrentTextEntry(nullptr);
876 }
877
878 Window XIMController::GetCurrentWindow() const
879@@ -131,12 +139,13 @@
880 void XIMController::SetupXIM()
881 {
882 xim_ = XOpenIM(display_, NULL, NULL, NULL);
883+
884 if (xim_)
885 {
886 SetupXIMDestroyedCallback();
887
888 if (window_)
889- xic_client_.ResetXIC(xim_, window_);
890+ xic_client_.ResetXIC(xim_, window_, display_);
891
892 XUnregisterIMInstantiateCallback (display_, NULL, NULL, NULL,
893 XIMController::SetupXIMClientCallback,
894
895=== renamed file 'NuxGraphics/XIMController.h' => 'Nux/XIMController.h'
896--- NuxGraphics/XIMController.h 2013-10-10 23:36:28 +0000
897+++ Nux/XIMController.h 2014-01-03 16:37:28 +0000
898@@ -1,5 +1,5 @@
899 /*
900-* Copyright 2012 Inalogic® Inc.
901+* Copyright 2012-2013 Inalogic® Inc.
902 *
903 * This program is free software: you can redistribute it and/or modify it
904 * under the terms of the GNU Lesser General Public License, as
905@@ -29,6 +29,7 @@
906
907 namespace nux
908 {
909+ class TextEntry;
910
911 class XIMController
912 {
913@@ -37,6 +38,7 @@
914 ~XIMController();
915
916 void SetFocusedWindow(Window window);
917+ void SetCurrentTextEntry(TextEntry* text_entry_);
918 void RemoveFocusedWindow();
919
920 bool IsXICValid() const;
921
922=== modified file 'NuxGraphics/GraphicsDisplayX11.cpp'
923--- NuxGraphics/GraphicsDisplayX11.cpp 2013-10-11 13:07:50 +0000
924+++ NuxGraphics/GraphicsDisplayX11.cpp 2014-01-03 16:37:28 +0000
925@@ -108,6 +108,7 @@
926 , m_X11Screen(0)
927 , m_X11Window(0)
928 , m_X11VisualInfo(NULL)
929+ , m_current_xic(NULL)
930 , parent_window_(0)
931 , m_GLCtx(NULL)
932 #ifndef NUX_OPENGLES_20
933@@ -172,9 +173,6 @@
934 NUX_SAFE_DELETE( m_GraphicsContext );
935 NUX_SAFE_DELETE( m_DeviceFactory );
936
937- // The XIM Controller needs to clean up before ~GraphicsDisplayX11
938- m_xim_controller.reset();
939-
940 if (m_CreatedFromForeignWindow == false)
941 {
942 DestroyOpenGLWindow();
943@@ -258,16 +256,6 @@
944 }
945 #endif
946
947- void GraphicsDisplay::XICFocus()
948- {
949- m_xim_controller->FocusInXIC();
950- }
951-
952- void GraphicsDisplay::XICUnFocus()
953- {
954- m_xim_controller->FocusOutXIC();
955- }
956-
957 // TODO: change windowWidth, windowHeight, to window_size;
958 static NCriticalSection CreateOpenGLWindow_CriticalSection;
959 bool GraphicsDisplay::CreateOpenGLWindow(const char* window_title,
960@@ -659,16 +647,6 @@
961 //XMapRaised(m_X11Display, m_X11Window);
962 }
963
964- m_xim_controller = std::make_shared<XIMController>(m_X11Display);
965- m_xim_controller->SetFocusedWindow(m_X11Window);
966-
967- if (m_xim_controller->IsXICValid())
968- {
969- long im_event_mask=0;
970- XGetICValues(m_xim_controller->GetXIC(), XNFilterEvents, &im_event_mask, NULL);
971- m_X11Attr.event_mask |= im_event_mask;
972- }
973-
974 #ifndef NUX_OPENGLES_20
975 if (_has_glx_13)
976 {
977@@ -778,8 +756,6 @@
978
979 gfx_interface_created_ = true;
980
981- m_xim_controller = std::make_shared<XIMController>(m_X11Display);
982-
983 // m_DeviceFactory = new GpuDevice(viewport_size_.GetWidth(), viewport_size_.GetHeight(), BITFMT_R8G8B8A8);
984 m_DeviceFactory = new GpuDevice(viewport_size_.width, viewport_size_.height, BITFMT_R8G8B8A8,
985 m_X11Display,
986@@ -811,14 +787,9 @@
987 return m_DeviceFactory;
988 }
989
990- void GraphicsDisplay::SetFocusedWindowForXIMController(Window window)
991- {
992- m_xim_controller->SetFocusedWindow(window);
993- }
994-
995- void GraphicsDisplay::RemoveFocusedWindowForXIMController()
996- {
997- m_xim_controller->RemoveFocusedWindow();
998+ void GraphicsDisplay::SetCurrentXIC(XIC xic)
999+ {
1000+ m_current_xic = xic;
1001 }
1002
1003 int GraphicsDisplay::GetGlXMajor() const
1004@@ -1359,14 +1330,10 @@
1005 bool bProcessEvent = true;
1006 XNextEvent(m_X11Display, &xevent);
1007
1008- if ((xevent.type == KeyPress || xevent.type == KeyRelease) &&
1009- m_xim_controller->GetCurrentWindow() != xevent.xkey.window)
1010- {
1011- m_xim_controller->SetFocusedWindow(xevent.xkey.window);
1012- }
1013-
1014 if (XFilterEvent(&xevent, None) == True)
1015+ {
1016 return true;
1017+ }
1018
1019 if (!_event_filters.empty())
1020 {
1021@@ -1687,7 +1654,6 @@
1022 m_pEvent->virtual_code = 0;
1023 //nuxDebugMsg("[GraphicsDisplay::ProcessXEvents]: FocusIn event.");
1024
1025- m_xim_controller->FocusInXIC();
1026 break;
1027 }
1028
1029@@ -1704,7 +1670,6 @@
1030 m_pEvent->virtual_code = 0;
1031 //nuxDebugMsg("[GraphicsDisplay::ProcessXEvents]: FocusOut event.");
1032
1033- m_xim_controller->FocusOutXIC();
1034 break;
1035 }
1036
1037@@ -1731,20 +1696,20 @@
1038 (keysym == NUX_VK_ESCAPE))
1039 {
1040 //temporary fix for TextEntry widget: filter some keys
1041- skip = true;
1042+ skip = true;
1043 }
1044
1045 if (!skip)
1046 {
1047 int num_char_stored = 0;
1048
1049- if (m_xim_controller->IsXICValid())
1050+ if (m_current_xic)
1051 {
1052 delete[] m_pEvent->dtext;
1053 m_pEvent->dtext = nullptr;
1054 Status status;
1055
1056- num_char_stored = XmbLookupString(m_xim_controller->GetXIC(), &xevent.xkey, nullptr,
1057+ num_char_stored = XmbLookupString(m_current_xic, &xevent.xkey, nullptr,
1058 0, (KeySym*) &m_pEvent->x11_keysym, &status);
1059
1060 if (status == XLookupKeySym)
1061@@ -1755,7 +1720,7 @@
1062 {
1063 int buf_len = num_char_stored + 1;
1064 m_pEvent->dtext = new char[buf_len];
1065- num_char_stored = XmbLookupString(m_xim_controller->GetXIC(), &xevent.xkey, m_pEvent->dtext,
1066+ num_char_stored = XmbLookupString(m_current_xic, &xevent.xkey, m_pEvent->dtext,
1067 buf_len, (KeySym*) &m_pEvent->x11_keysym, nullptr);
1068
1069 m_pEvent->dtext[num_char_stored] = 0;
1070
1071=== modified file 'NuxGraphics/GraphicsDisplayX11.h'
1072--- NuxGraphics/GraphicsDisplayX11.h 2012-12-17 19:04:50 +0000
1073+++ NuxGraphics/GraphicsDisplayX11.h 2014-01-03 16:37:28 +0000
1074@@ -27,7 +27,6 @@
1075 #include "GLTimer.h"
1076 #include "GLDeviceObjects.h"
1077 #include "GLRenderStates.h"
1078-#include "XIMController.h"
1079
1080 /* Xlib.h is the default header that is included and has the core functionallity */
1081 #include <X11/Xlib.h>
1082@@ -85,7 +84,7 @@
1083 Window m_X11Window;
1084 XVisualInfo *m_X11VisualInfo;
1085
1086- std::shared_ptr<XIMController> m_xim_controller;
1087+ XIC m_current_xic;
1088
1089 int parent_window_;
1090 #ifndef NUX_OPENGLES_20
1091@@ -286,8 +285,7 @@
1092
1093 GpuDevice* GetGpuDevice() const;
1094
1095- void SetFocusedWindowForXIMController(Window window);
1096- void RemoveFocusedWindowForXIMController();
1097+ void SetCurrentXIC(XIC xic);
1098
1099 // Dialog
1100 /*bool StartOpenFileDialog(FileDialogOption& fdo);
1101@@ -348,8 +346,6 @@
1102
1103 void * KeyboardGrabData() { return _global_keyboard_grab_data; }
1104 void * PointerGrabData() { return _global_pointer_grab_data; }
1105- void XICFocus();
1106- void XICUnFocus();
1107
1108 private:
1109 void InitGlobalGrabWindow();
1110
1111=== modified file 'NuxGraphics/Makefile.am'
1112--- NuxGraphics/Makefile.am 2012-12-05 19:22:13 +0000
1113+++ NuxGraphics/Makefile.am 2014-01-03 16:37:28 +0000
1114@@ -93,9 +93,7 @@
1115 source_h += \
1116 $(srcdir)/GraphicsDisplayX11.h \
1117 $(srcdir)/VirtualKeyCodesX11.h \
1118- $(srcdir)/XInputWindow.h \
1119- $(srcdir)/XICClient.h \
1120- $(srcdir)/XIMController.h
1121+ $(srcdir)/XInputWindow.h
1122 endif
1123
1124 if !NUX_OPENGLES_20
1125@@ -170,9 +168,7 @@
1126 if USE_X11
1127 source_cpp += \
1128 $(srcdir)/GraphicsDisplayX11.cpp \
1129- $(srcdir)/XInputWindow.cpp \
1130- $(srcdir)/XICClient.cpp \
1131- $(srcdir)/XIMController.cpp
1132+ $(srcdir)/XInputWindow.cpp
1133 endif
1134
1135 if !NUX_OPENGLES_20
1136
1137=== modified file 'NuxGraphics/XInputWindow.cpp'
1138--- NuxGraphics/XInputWindow.cpp 2013-07-18 23:07:45 +0000
1139+++ NuxGraphics/XInputWindow.cpp 2014-01-03 16:37:28 +0000
1140@@ -22,7 +22,6 @@
1141 #include "XInputWindow.h"
1142 #include "GraphicsDisplayX11.h"
1143 #include "GLThread.h"
1144-#include "XIMController.h"
1145
1146 namespace nux
1147 {
1148@@ -396,8 +395,6 @@
1149
1150 void XInputWindow::Hide()
1151 {
1152- GetGraphicsDisplay()->RemoveFocusedWindowForXIMController();
1153-
1154 XMoveResizeWindow(display_, window_,
1155 -100 - geometry_.width,
1156 -100 - geometry_.height,
1157@@ -408,8 +405,6 @@
1158
1159 void XInputWindow::Show()
1160 {
1161- GetGraphicsDisplay()->SetFocusedWindowForXIMController(window_);
1162-
1163 shown_ = true;
1164 if (!mapped_)
1165 {
1166
1167=== modified file 'configure.ac'
1168--- configure.ac 2013-10-29 22:13:58 +0000
1169+++ configure.ac 2014-01-03 16:37:28 +0000
1170@@ -15,7 +15,7 @@
1171 #
1172 m4_define([nux_major_version], [4])
1173 m4_define([nux_minor_version], [0])
1174-m4_define([nux_micro_version], [4])
1175+m4_define([nux_micro_version], [5])
1176
1177 m4_define([nux_api_version], [4.0])
1178 # Increase the number (to the current date) everytime you propose a branch that breaks the API or ABI
1179@@ -23,7 +23,7 @@
1180 # e.g.: december 5th, 2011 is: 20111205
1181 # To make more than one API change in a day, add a number to the date. Like 20111205.xx
1182
1183-m4_define([nux_abi_version], [20131029.0])
1184+m4_define([nux_abi_version], [20131203.0])
1185 m4_define([nux_version],
1186 [nux_major_version.nux_minor_version.nux_micro_version])
1187
1188
1189=== modified file 'debian/changelog'
1190--- debian/changelog 2013-10-29 22:13:58 +0000
1191+++ debian/changelog 2014-01-03 16:37:28 +0000
1192@@ -1,3 +1,11 @@
1193+nux (4.0.5-0ubuntu1) UNRELEASED; urgency=low
1194+
1195+ [ Brandon Schaefer ]
1196+ * Add XIM preedit support
1197+ * Bump version to 4.0.5
1198+
1199+ -- Brandon Schaefer <brandon.schaefer@canonical.com> Fri, 03 Jan 2014 08:31:35 -0800
1200+
1201 nux (4.0.4-0ubuntu1) UNRELEASED; urgency=low
1202
1203 [ Sam Spilsbury ]

Subscribers

People subscribed via source and target branches