Merge lp:~yuningdodo/nux/im-interface into lp:~wengxt/nux/fcitx-support
- im-interface
- Merge into fcitx-support
Status: | Merged |
---|---|
Approved by: | csslayer |
Approved revision: | 635 |
Merged at revision: | 632 |
Proposed branch: | lp:~yuningdodo/nux/im-interface |
Merge into: | lp:~wengxt/nux/fcitx-support |
Diff against target: |
966 lines (+873/-2) 8 files modified
Nux/InputMethod.cpp (+5/-0) Nux/InputMethodXim.cpp (+202/-0) Nux/InputMethodXim.h (+69/-0) Nux/Makefile.am (+6/-2) Nux/TextEntry.h (+2/-0) Nux/XimClientData.cpp (+524/-0) Nux/XimClientData.h (+64/-0) NuxGraphics/GraphicsDisplayX11.h (+1/-0) |
To merge this branch: | bzr merge lp:~yuningdodo/nux/im-interface |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
csslayer | Approve | ||
Yu Ning (community) | Approve | ||
Review via email: mp+115591@code.launchpad.net |
Commit message
Description of the change
Hi wengxt,
I had merged the XIM patches from lp:~paulliu/nux/nux and lp:~yuningdodo/nux/xim-preedit to your branch. However the initialize and events handling of XIM are still done in GraphicsDisplayX11. Further split of the code requires me to get more knowledge on X, this would take some time.
Please review my patch and ask me to improve it.
Thanks.
Yu Ning
By the way, I still think it is not quite suitable to add fcitx support inside nux, it would be much better if we build the fcitx code outside nux, but this would need nux to provide the ability to scan & load plugins, which should be discussed further with the nux-team.
csslayer (wengxt) wrote : | # |
- 631. By Yu Ning
-
Move XIM events handling from GraphicsDisplayX11 to InputMethodXim.
We also:
* Add implement of InputMethodXim::UpdateCursorLo cation( ).
* Add some error checking in XimClientData. - 632. By Yu Ning
-
Move XFilterEvent() from GraphicsDisplayX11 to InputMethodXim.
- 633. By Yu Ning
-
Merge from lp:~wengxt/nux/fcitx-support
- 634. By Yu Ning
-
Seperate Xim from GraphicsDisplayX11 completely.
Yu Ning (yuningdodo) wrote : | # |
Hi csslayer,
Thanks for your hint about the x11 event filter, and your work to make the im single-instance.
I had just updated my branch, separate xim stuff from GraphicsDisplayX11 completely. However I didn't mix XimClientData into XimIMEContext to keep the code clean.
Please review again.
Many thanks.
Yu Ning
csslayer (wengxt) wrote : | # |
Sorry for late, I'm out for travel last week.
Looks good for me.
Yu Ning (yuningdodo) wrote : | # |
Hi csslayer,
Welcome back. Thanks for approving the branch. Hopes your branch can be finally merged into the main branch.
Preview Diff
1 | === modified file 'Nux/InputMethod.cpp' |
2 | --- Nux/InputMethod.cpp 2012-07-18 17:44:33 +0000 |
3 | +++ Nux/InputMethod.cpp 2012-07-20 05:33:28 +0000 |
4 | @@ -26,6 +26,7 @@ |
5 | #include "InputMethod.h" |
6 | #include "InputMethodIBus.h" |
7 | #include "InputMethodFcitx.h" |
8 | +#include "InputMethodXim.h" |
9 | |
10 | namespace nux |
11 | { |
12 | @@ -71,6 +72,10 @@ |
13 | global_context_ = new IBusIMEContext(); |
14 | else if (g_strcmp0(im, "fcitx") == 0) |
15 | global_context_ = new FcitxIMEContext(); |
16 | + else if (g_strcmp0(im, "xim") == 0) |
17 | + global_context_ = new XimIMEContext(); |
18 | + else if (g_getenv("XMODIFIERS")) |
19 | + global_context_ = new XimIMEContext(); |
20 | else |
21 | global_context_ = NULL; |
22 | // global_context_ = XimIMEContext(); |
23 | |
24 | === added file 'Nux/InputMethodXim.cpp' |
25 | --- Nux/InputMethodXim.cpp 1970-01-01 00:00:00 +0000 |
26 | +++ Nux/InputMethodXim.cpp 2012-07-20 05:33:28 +0000 |
27 | @@ -0,0 +1,202 @@ |
28 | +/* |
29 | +* Copyright 2010 Inalogic® Inc. |
30 | +* |
31 | +* This program is free software: you can redistribute it and/or modify it |
32 | +* under the terms of the GNU Lesser General Public License, as |
33 | +* published by the Free Software Foundation; either version 2.1 or 3.0 |
34 | +* of the License. |
35 | +* |
36 | +* This program is distributed in the hope that it will be useful, but |
37 | +* WITHOUT ANY WARRANTY; without even the implied warranties of |
38 | +* MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
39 | +* PURPOSE. See the applicable version of the GNU Lesser General Public |
40 | +* License for more details. |
41 | +* |
42 | +* You should have received a copy of both the GNU Lesser General Public |
43 | +* License along with this program. If not, see <http://www.gnu.org/licenses/> |
44 | +* |
45 | +* Authored by: Weng Xuetian <wengxt@gmail.com> |
46 | +* Yu Ning <yuningdodo@gmail.com> |
47 | +* |
48 | +*/ |
49 | + |
50 | +#include "Nux.h" |
51 | + |
52 | +#include "InputMethodXim.h" |
53 | + |
54 | +namespace nux |
55 | +{ |
56 | + extern unsigned int GetModifierKeyState(unsigned int); |
57 | + |
58 | + XimIMEContext::XimIMEContext() |
59 | + : IMEContext() |
60 | + { |
61 | + // XIM support |
62 | + memset(&m_ximData,0,sizeof(m_ximData)); |
63 | + m_ximData.window = nux::GetGraphicsDisplay()->GetWindowHandle(); |
64 | + m_ximData.display = nux::GetGraphicsDisplay()->GetX11Display(); |
65 | + m_ximData.InitXIM(); |
66 | + |
67 | + long im_event_mask=0; |
68 | + if (m_ximData.m_xic) |
69 | + { |
70 | + XGetICValues(m_ximData.m_xic, XNFilterEvents, &im_event_mask, NULL); |
71 | + nux::GetGraphicsDisplay()->m_X11Attr.event_mask |= im_event_mask; |
72 | + } |
73 | + |
74 | + event_filter_.filter = XimIMEContext::XEventFilter; |
75 | + event_filter_.data = this; |
76 | + nux::GetGraphicsDisplay()->AddEventFilter(event_filter_); |
77 | + |
78 | + // todo(csslayer) forward key, surrounding text |
79 | + } |
80 | + |
81 | + XimIMEContext::~XimIMEContext() |
82 | + { |
83 | + nux::GetGraphicsDisplay()->RemoveEventFilter(this); |
84 | + m_ximData.UninitXIM(); |
85 | + |
86 | + TextEntry* text_entry = FocusWidget(); |
87 | + if (!text_entry) |
88 | + return; |
89 | + text_entry->ResetPreedit(); |
90 | + } |
91 | + |
92 | + void XimIMEContext::SetFocusWidget(TextEntry* text_entry) |
93 | + { |
94 | + TextEntry* old_entry_ = FocusWidget(); |
95 | + |
96 | + if (old_entry_ == text_entry) |
97 | + return; |
98 | + |
99 | + if (old_entry_) |
100 | + { |
101 | + old_entry_->ime_active_ = false; |
102 | + if (IsConnected()) |
103 | + m_ximData.XICUnFocus(old_entry_); |
104 | + } |
105 | + |
106 | + IMEContext::SetFocusWidget(text_entry); |
107 | + |
108 | + bool has_focus = (text_entry != NULL); |
109 | + |
110 | + if (!IsConnected()) |
111 | + return; |
112 | + |
113 | + if (has_focus) |
114 | + { |
115 | + text_entry->ime_active_ = true; |
116 | + m_ximData.XICFocus(text_entry); |
117 | + } |
118 | + else |
119 | + { |
120 | + m_ximData.XICUnFocus(text_entry); |
121 | + } |
122 | + } |
123 | + |
124 | + void XimIMEContext::Reset() |
125 | + { |
126 | + if (IsConnected()) |
127 | + m_ximData.Reset(); |
128 | + } |
129 | + |
130 | + bool XimIMEContext::FilterKeyEvent(const KeyEvent& event) |
131 | + { |
132 | + return false; |
133 | + } |
134 | + |
135 | + void XimIMEContext::SetSurrounding(const std::wstring& text, int cursor_pos) |
136 | + { |
137 | + // TODO(penghuang) support surrounding |
138 | + } |
139 | + |
140 | + bool XimIMEContext::IsConnected() const |
141 | + { |
142 | + return !!m_ximData.m_xic; |
143 | + } |
144 | + |
145 | + void XimIMEContext::UpdateCursorLocation() |
146 | + { |
147 | + nux::Rect strong, weak; |
148 | + TextEntry* text_entry = FocusWidget(); |
149 | + if (!text_entry) |
150 | + return; |
151 | + text_entry->GetCursorRects(&strong, &weak); |
152 | + |
153 | + // Get the position of the TextEntry in the Window. |
154 | + nux::Geometry geo = text_entry->GetAbsoluteGeometry(); |
155 | + |
156 | + // Get the Geometry of the window on the display. |
157 | + nux::Geometry window_geo = nux::GetGraphicsDisplay()->GetWindowGeometry(); |
158 | + |
159 | + if (IsConnected()) |
160 | + m_ximData.SetCursorLocation(geo.x + window_geo.x + strong.x, geo.y + window_geo.y, 0, 0); |
161 | + } |
162 | + |
163 | + bool XimIMEContext::XEventFilter(XEvent xevent, void * data) |
164 | + { |
165 | + XimIMEContext *context = (XimIMEContext*)data; |
166 | + GraphicsDisplay *graphics_display = nux::GetGraphicsDisplay(); |
167 | + Event *m_pEvent = graphics_display->m_pEvent; |
168 | + |
169 | + if (!context->IsConnected() || !context->m_ximData.textentry) |
170 | + return false; |
171 | + |
172 | + if (XFilterEvent(&xevent, None) == True) |
173 | + return true; |
174 | + |
175 | + switch(xevent.type) |
176 | + { |
177 | + case KeyPress: |
178 | + { |
179 | + //nuxDebugMsg("[InputMethodXim::XEventFilter]: KeyPress event."); |
180 | + KeyCode keycode = xevent.xkey.keycode; |
181 | + KeySym keysym = NoSymbol; |
182 | + keysym = XKeycodeToKeysym(xevent.xany.display, keycode, 0); |
183 | + |
184 | + m_pEvent->key_modifiers = GetModifierKeyState(xevent.xkey.state); |
185 | + m_pEvent->key_repeat_count = 0; |
186 | + m_pEvent->x11_keysym = keysym; |
187 | + m_pEvent->x11_keycode = xevent.xkey.keycode; |
188 | + m_pEvent->type = NUX_KEYDOWN; |
189 | + m_pEvent->x11_timestamp = xevent.xkey.time; |
190 | + m_pEvent->x11_key_state = xevent.xkey.state; |
191 | + |
192 | + char buffer[NUX_EVENT_TEXT_BUFFER_SIZE]; |
193 | + Memset(m_pEvent->text, 0, NUX_EVENT_TEXT_BUFFER_SIZE); |
194 | + |
195 | + bool skip = false; |
196 | + if ((keysym == NUX_VK_BACKSPACE) || |
197 | + (keysym == NUX_VK_DELETE) || |
198 | + (keysym == NUX_VK_ESCAPE)) |
199 | + { |
200 | + //temporary fix for TextEntry widget: filter some keys |
201 | + skip = true; |
202 | + } |
203 | + |
204 | + int num_char_stored = XmbLookupString(context->m_ximData.m_xic, &xevent.xkey, buffer, NUX_EVENT_TEXT_BUFFER_SIZE, (KeySym*) &m_pEvent->x11_keysym, NULL); |
205 | + if (num_char_stored && (!skip)) |
206 | + { |
207 | + Memcpy(m_pEvent->text, buffer, num_char_stored); |
208 | + } |
209 | + |
210 | + return true; |
211 | + } |
212 | + default: |
213 | + { |
214 | + break; |
215 | + } |
216 | + } |
217 | + |
218 | + return false; |
219 | + } |
220 | + |
221 | + bool XimIMEContext::IsHotkeyEvent(EventType type, unsigned long keysym, unsigned long modifiers) const |
222 | + { |
223 | + return false; |
224 | + } |
225 | + |
226 | + bool XimIMEContext::IsIgnoredKey(unsigned long keysym, unsigned long modifiers) const { |
227 | + return false; |
228 | + } |
229 | +} |
230 | |
231 | === added file 'Nux/InputMethodXim.h' |
232 | --- Nux/InputMethodXim.h 1970-01-01 00:00:00 +0000 |
233 | +++ Nux/InputMethodXim.h 2012-07-20 05:33:28 +0000 |
234 | @@ -0,0 +1,69 @@ |
235 | +/* |
236 | +* Copyright 2010 Inalogic® Inc. |
237 | +* |
238 | +* This program is free software: you can redistribute it and/or modify it |
239 | +* under the terms of the GNU Lesser General Public License, as |
240 | +* published by the Free Software Foundation; either version 2.1 or 3.0 |
241 | +* of the License. |
242 | +* |
243 | +* This program is distributed in the hope that it will be useful, but |
244 | +* WITHOUT ANY WARRANTY; without even the implied warranties of |
245 | +* MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
246 | +* PURPOSE. See the applicable version of the GNU Lesser General Public |
247 | +* License for more details. |
248 | +* |
249 | +* You should have received a copy of both the GNU Lesser General Public |
250 | +* License along with this program. If not, see <http://www.gnu.org/licenses/> |
251 | +* |
252 | +* Authored by: Weng Xuetian <wengxt@gmail.com> |
253 | +* Yu Ning <yuningdodo@gmail.com> |
254 | +* |
255 | +*/ |
256 | + |
257 | + |
258 | +#ifndef INPUTMETHODXIM_H |
259 | +#define INPUTMETHODXIM_H |
260 | + |
261 | +#include <Nux/InputMethod.h> |
262 | +#include <Nux/XimClientData.h> |
263 | + |
264 | +namespace nux |
265 | +{ |
266 | + |
267 | + class XimIMEContext; |
268 | + class TextEntry; |
269 | + |
270 | + // Implements IMEContext to support XIM protocol |
271 | + class XimIMEContext : public IMEContext |
272 | + { |
273 | + public: |
274 | + explicit XimIMEContext(); |
275 | + virtual ~XimIMEContext(); |
276 | + |
277 | + // views::IMEContext implementations: |
278 | + virtual void SetFocusWidget(TextEntry* text_entry); |
279 | + virtual void Reset(); |
280 | + virtual bool FilterKeyEvent(const KeyEvent& event); |
281 | + virtual void SetSurrounding(const std::wstring& text, int cursor_pos); |
282 | + |
283 | + virtual bool IsConnected() const; |
284 | + virtual bool IsHotkeyEvent(EventType type, unsigned long keysym, unsigned long modifiers) const; |
285 | + virtual bool IsIgnoredKey(unsigned long keysym, unsigned long modifiers) const; |
286 | + |
287 | + protected: |
288 | + private: |
289 | + void UpdateCursorLocation(); |
290 | + |
291 | + static bool XEventFilter(XEvent xevent, void * data); |
292 | + |
293 | + struct XimClientData m_ximData; |
294 | + GraphicsDisplay::EventFilterArg event_filter_; |
295 | + bool is_focused_; |
296 | + |
297 | + XimIMEContext(const XimIMEContext&); |
298 | + void operator = (const XimIMEContext&); |
299 | + }; |
300 | + |
301 | +} |
302 | +#endif // INPUTMETHODXIM_H |
303 | + |
304 | |
305 | === modified file 'Nux/Makefile.am' |
306 | --- Nux/Makefile.am 2012-07-10 01:39:06 +0000 |
307 | +++ Nux/Makefile.am 2012-07-20 05:33:28 +0000 |
308 | @@ -99,6 +99,7 @@ |
309 | $(srcdir)/InputMethod.cpp \ |
310 | $(srcdir)/InputMethodIBus.cpp \ |
311 | $(srcdir)/InputMethodFcitx.cpp \ |
312 | + $(srcdir)/InputMethodXim.cpp \ |
313 | $(srcdir)/TextLoader.cpp \ |
314 | $(srcdir)/TextureArea.cpp \ |
315 | $(srcdir)/Timeline.cpp \ |
316 | @@ -114,7 +115,8 @@ |
317 | $(srcdir)/VSplitter.cpp \ |
318 | $(srcdir)/WidgetMetrics.cpp \ |
319 | $(srcdir)/WindowCompositor.cpp \ |
320 | - $(srcdir)/WindowThread.cpp |
321 | + $(srcdir)/WindowThread.cpp \ |
322 | + $(srcdir)/XimClientData.cpp |
323 | |
324 | source_h = \ |
325 | $(builddir)/ABI.h \ |
326 | @@ -188,6 +190,7 @@ |
327 | $(srcdir)/InputMethod.h \ |
328 | $(srcdir)/InputMethodIBus.h \ |
329 | $(srcdir)/InputMethodFcitx.h \ |
330 | + $(srcdir)/InputMethodXim.h \ |
331 | $(srcdir)/TextLoader.h \ |
332 | $(srcdir)/TextureArea.h \ |
333 | $(srcdir)/Timeline.h \ |
334 | @@ -203,7 +206,8 @@ |
335 | $(srcdir)/VSplitter.h \ |
336 | $(srcdir)/WidgetMetrics.h \ |
337 | $(srcdir)/WindowCompositor.h \ |
338 | - $(srcdir)/WindowThread.h |
339 | + $(srcdir)/WindowThread.h \ |
340 | + $(srcdir)/XimClientData.h |
341 | |
342 | nuxprogramframework_cpp = \ |
343 | $(srcdir)/ProgramFramework/ProgramTemplate.cpp \ |
344 | |
345 | === modified file 'Nux/TextEntry.h' |
346 | --- Nux/TextEntry.h 2012-07-17 17:38:14 +0000 |
347 | +++ Nux/TextEntry.h 2012-07-20 05:33:28 +0000 |
348 | @@ -479,6 +479,8 @@ |
349 | friend class IMEContext; |
350 | friend class IBusIMEContext; |
351 | friend class FcitxIMEContext; |
352 | + friend class XimIMEContext; |
353 | + friend struct XimClientData; |
354 | #endif |
355 | |
356 | bool ime_active_; |
357 | |
358 | === added file 'Nux/XimClientData.cpp' |
359 | --- Nux/XimClientData.cpp 1970-01-01 00:00:00 +0000 |
360 | +++ Nux/XimClientData.cpp 2012-07-20 05:33:28 +0000 |
361 | @@ -0,0 +1,524 @@ |
362 | +/* |
363 | +* Copyright 2010 Inalogic® Inc. |
364 | +* |
365 | +* This program is free software: you can redistribute it and/or modify it |
366 | +* under the terms of the GNU Lesser General Public License, as |
367 | +* published by the Free Software Foundation; either version 2.1 or 3.0 |
368 | +* of the License. |
369 | +* |
370 | +* This program is distributed in the hope that it will be useful, but |
371 | +* WITHOUT ANY WARRANTY; without even the implied warranties of |
372 | +* MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
373 | +* PURPOSE. See the applicable version of the GNU Lesser General Public |
374 | +* License for more details. |
375 | +* |
376 | +* You should have received a copy of both the GNU Lesser General Public |
377 | +* License along with this program. If not, see <http://www.gnu.org/licenses/> |
378 | +* |
379 | +* Authored by: Ying-Chun Liu <paul.liu@canonical.com> |
380 | +* Yu Ning <yuningdodo@gmail.com> |
381 | +* |
382 | +*/ |
383 | + |
384 | +#include "Nux.h" |
385 | +#include "TextEntry.h" |
386 | + |
387 | +#include "XimClientData.h" |
388 | + |
389 | +namespace nux |
390 | +{ |
391 | + |
392 | +#define PREEDIT_MASK (XIMPreeditCallbacks | XIMPreeditPosition | \ |
393 | + XIMPreeditArea | XIMPreeditNothing | XIMPreeditNone) |
394 | +#define STATUS_MASK (XIMStatusCallbacks | XIMStatusArea | \ |
395 | + XIMStatusNothing | XIMStatusNone) |
396 | +#define ALLOWED_MASK (XIMPreeditCallbacks | XIMPreeditNothing | XIMPreeditNone | \ |
397 | + XIMStatusCallbacks | XIMStatusNothing | XIMStatusNone) |
398 | + |
399 | + /* stolen from gtk+2.24.4/modules/input/gtkimcontextxim.c */ |
400 | + static XIMStyle |
401 | + choose_better_style (XIMStyle style1, XIMStyle style2) |
402 | + { |
403 | + XIMStyle s1, s2, u; |
404 | + |
405 | + if (style1 == 0) return style2; |
406 | + if (style2 == 0) return style1; |
407 | + if ((style1 & (PREEDIT_MASK | STATUS_MASK)) |
408 | + == (style2 & (PREEDIT_MASK | STATUS_MASK))) |
409 | + return style1; |
410 | + |
411 | + s1 = style1 & PREEDIT_MASK; |
412 | + s2 = style2 & PREEDIT_MASK; |
413 | + u = s1 | s2; |
414 | + if (s1 != s2) { |
415 | + if (u & XIMPreeditCallbacks) |
416 | + return (s1 == XIMPreeditCallbacks) ? style1 : style2; |
417 | + else if (u & XIMPreeditPosition) |
418 | + return (s1 == XIMPreeditPosition) ? style1 :style2; |
419 | + else if (u & XIMPreeditArea) |
420 | + return (s1 == XIMPreeditArea) ? style1 : style2; |
421 | + else if (u & XIMPreeditNothing) |
422 | + return (s1 == XIMPreeditNothing) ? style1 : style2; |
423 | + else if (u & XIMPreeditNone) |
424 | + return (s1 == XIMPreeditNone) ? style1 : style2; |
425 | + } else { |
426 | + s1 = style1 & STATUS_MASK; |
427 | + s2 = style2 & STATUS_MASK; |
428 | + u = s1 | s2; |
429 | + if (u & XIMStatusCallbacks) |
430 | + return (s1 == XIMStatusCallbacks) ? style1 : style2; |
431 | + else if (u & XIMStatusArea) |
432 | + return (s1 == XIMStatusArea) ? style1 : style2; |
433 | + else if (u & XIMStatusNothing) |
434 | + return (s1 == XIMStatusNothing) ? style1 : style2; |
435 | + else if (u & XIMStatusNone) |
436 | + return (s1 == XIMStatusNone) ? style1 : style2; |
437 | + } |
438 | + return 0; /* Get rid of stupid warning */ |
439 | + } |
440 | + |
441 | +/* Mask of feedback bits that we render |
442 | + */ |
443 | +#define FEEDBACK_MASK (XIMReverse | XIMUnderline) |
444 | + |
445 | + /* stolen from gtk+2.24.4/modules/input/gtkimcontextxim.c */ |
446 | + static void |
447 | + add_feedback_attr (PangoAttrList *attrs, |
448 | + const gchar *str, |
449 | + XIMFeedback feedback, |
450 | + gint start_pos, |
451 | + gint end_pos) |
452 | + { |
453 | + PangoAttribute *attr; |
454 | + |
455 | + gint start_index = g_utf8_offset_to_pointer (str, start_pos) - str; |
456 | + gint end_index = g_utf8_offset_to_pointer (str, end_pos) - str; |
457 | + |
458 | + if (feedback & XIMUnderline) |
459 | + { |
460 | + attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); |
461 | + attr->start_index = start_index; |
462 | + attr->end_index = end_index; |
463 | + |
464 | + pango_attr_list_change (attrs, attr); |
465 | + } |
466 | + |
467 | + if (feedback & XIMReverse) |
468 | + { |
469 | + attr = pango_attr_foreground_new (0xffff, 0xffff, 0xffff); |
470 | + attr->start_index = start_index; |
471 | + attr->end_index = end_index; |
472 | + |
473 | + pango_attr_list_change (attrs, attr); |
474 | + |
475 | + attr = pango_attr_background_new (0, 0, 0); |
476 | + attr->start_index = start_index; |
477 | + attr->end_index = end_index; |
478 | + |
479 | + pango_attr_list_change (attrs, attr); |
480 | + } |
481 | + |
482 | + if (feedback & ~FEEDBACK_MASK) |
483 | + g_warning ("Unrendered feedback style: %#lx", feedback & ~FEEDBACK_MASK); |
484 | + } |
485 | + |
486 | + /* derived from gtk+2.24.4/modules/input/gtkimcontextxim.c: gtk_im_context_xim_get_preedit_string() */ |
487 | + static void |
488 | + feedbacks_to_pango_attr_list(PangoAttrList **attrs, |
489 | + const char *utf8, |
490 | + int nfeedbacks, |
491 | + XIMFeedback *feedbacks) |
492 | + { |
493 | + int i; |
494 | + XIMFeedback last_feedback = 0; |
495 | + gint start = -1; |
496 | + |
497 | + if (attrs) |
498 | + { |
499 | + *attrs = pango_attr_list_new (); |
500 | + |
501 | + for (i = 0; i < nfeedbacks; i++) |
502 | + { |
503 | + XIMFeedback new_feedback = feedbacks[i] & FEEDBACK_MASK; |
504 | + if (new_feedback != last_feedback) |
505 | + { |
506 | + if (start >= 0) |
507 | + add_feedback_attr (*attrs, utf8, last_feedback, start, i); |
508 | + |
509 | + last_feedback = new_feedback; |
510 | + start = i; |
511 | + } |
512 | + } |
513 | + |
514 | + if (start >= 0) |
515 | + add_feedback_attr (*attrs, utf8, last_feedback, start, i); |
516 | + } |
517 | + } |
518 | + |
519 | + void XimClientData::InitXIM() |
520 | + { |
521 | +#if 0 |
522 | + const char *xmodifier; |
523 | + |
524 | + /* don't do anything if we are using ibus */ |
525 | + xmodifier = getenv("XMODIFIERS"); |
526 | + if (xmodifier && strstr(xmodifier,"ibus") != NULL) |
527 | + { |
528 | + nuxDebugMsg("[XimClientData::InitXIM] ibus natively supported"); |
529 | + return; |
530 | + } |
531 | +#endif |
532 | + |
533 | + if (setlocale(LC_ALL, "") == NULL) |
534 | + { |
535 | + nuxDebugMsg("[XimClientData::InitXIM] cannot setlocale"); |
536 | + } |
537 | + |
538 | + if (!XSupportsLocale()) |
539 | + { |
540 | + nuxDebugMsg("[XimClientData::InitXIM] no supported locale"); |
541 | + } |
542 | + |
543 | + if (XSetLocaleModifiers("") == NULL) |
544 | + { |
545 | + nuxDebugMsg("[XimClientData::InitXIM] XSetLocaleModifiers failed."); |
546 | + } |
547 | + |
548 | + if (!XRegisterIMInstantiateCallback(this->display, NULL, NULL, NULL, XimClientData::XIMStartCallback, (XPointer)(this))) |
549 | + { |
550 | + nuxDebugMsg("[XimClientData::InitXIM] Cannot Register IM Init callback"); |
551 | + } |
552 | + } |
553 | + |
554 | + void XimClientData::UninitXIM() |
555 | + { |
556 | + XUnregisterIMInstantiateCallback(this->display, NULL, NULL, NULL, |
557 | + XimClientData::XIMStartCallback, (XPointer)(this)); |
558 | + } |
559 | + |
560 | + /* derived from gtk+2.24.4/modules/input/gtkimcontextxim.c: gtk_im_context_xim_reset() */ |
561 | + void XimClientData::Reset() |
562 | + { |
563 | + XIC ic = this->m_xic; |
564 | + gchar *result; |
565 | + |
566 | + /* restore conversion state after resetting ic later */ |
567 | + XIMPreeditState preedit_state = XIMPreeditUnKnown; |
568 | + XVaNestedList preedit_attr; |
569 | + gboolean have_preedit_state = FALSE; |
570 | + |
571 | + if (!ic) |
572 | + return; |
573 | + |
574 | + |
575 | + preedit_attr = XVaCreateNestedList(0, |
576 | + XNPreeditState, &preedit_state, |
577 | + NULL); |
578 | + if (!XGetICValues(ic, |
579 | + XNPreeditAttributes, preedit_attr, |
580 | + NULL)) |
581 | + have_preedit_state = TRUE; |
582 | + |
583 | + XFree(preedit_attr); |
584 | + |
585 | + result = XmbResetIC (ic); |
586 | + |
587 | + preedit_attr = XVaCreateNestedList(0, |
588 | + XNPreeditState, preedit_state, |
589 | + NULL); |
590 | + if (have_preedit_state) |
591 | + XSetICValues(ic, |
592 | + XNPreeditAttributes, preedit_attr, |
593 | + NULL); |
594 | + |
595 | + XFree(preedit_attr); |
596 | + |
597 | + XFree (result); |
598 | + } |
599 | + |
600 | + void XimClientData::XICFocus(TextEntry *textentry) |
601 | + { |
602 | + if (this->m_xic) |
603 | + { |
604 | + XSetICFocus(this->m_xic); |
605 | + this->focus_stat=1; |
606 | + this->textentry = textentry; |
607 | + } |
608 | + } |
609 | + |
610 | + void XimClientData::XICUnFocus(TextEntry *textentry) |
611 | + { |
612 | + if (this->m_xic) |
613 | + { |
614 | + nuxAssert(this->textentry == textentry); |
615 | + XUnsetICFocus(this->m_xic); |
616 | + this->focus_stat=0; |
617 | + this->textentry = NULL; |
618 | + } |
619 | + } |
620 | + |
621 | + /* derived from gtk+2.24.4/modules/input/gtkimcontextxim.c: gtk_im_context_xim_set_cursor_location() */ |
622 | + void XimClientData::SetCursorLocation(int x, int y, int width, int height) |
623 | + { |
624 | + XIC ic = this->m_xic; |
625 | + |
626 | + XVaNestedList preedit_attr; |
627 | + XPoint spot; |
628 | + |
629 | + spot.x = x; |
630 | + spot.y = y; |
631 | + |
632 | + preedit_attr = XVaCreateNestedList (0, |
633 | + XNSpotLocation, &spot, |
634 | + NULL); |
635 | + XSetICValues (ic, |
636 | + XNPreeditAttributes, preedit_attr, |
637 | + NULL); |
638 | + XFree(preedit_attr); |
639 | + } |
640 | + |
641 | + void XimClientData::XIMEndCallback(Display *dpy, XPointer client_data, XPointer call_data) |
642 | + { |
643 | + struct XimClientData *data; |
644 | + |
645 | + data = (struct XimClientData*)client_data; |
646 | + data->m_xim = NULL; |
647 | + data->m_xic = NULL; |
648 | + data->textentry = NULL; |
649 | + } |
650 | + |
651 | + void XimClientData::XIMStartCallback(Display *dpy, XPointer client_data, XPointer call_data) |
652 | + { |
653 | + int i; |
654 | + XIMCallback destroy; |
655 | + XIMStyles *xim_styles = NULL; |
656 | + XIMStyle root_style = (XIMPreeditNothing|XIMStatusNothing); |
657 | + XIMStyle preferred_style = (XIMPreeditCallbacks|XIMStatusCallbacks); |
658 | + XIMStyle choosed_style = 0; |
659 | + XIMStyle im_style = 0; |
660 | + const char * name1 = NULL; |
661 | + XVaNestedList list1 = NULL; |
662 | + const char * name2 = NULL; |
663 | + XVaNestedList list2 = NULL; |
664 | + Window win; |
665 | + struct XimClientData *data; |
666 | + |
667 | + data = (struct XimClientData*)client_data; |
668 | + win = data->window; |
669 | + |
670 | + data->m_xim = XOpenIM(dpy, NULL, NULL, NULL); |
671 | + if (! (data->m_xim)) |
672 | + { |
673 | + nuxDebugMsg("[XimClientData::XIMStartCallback] Failed to open IM."); |
674 | + return; |
675 | + } |
676 | + memset(&destroy, 0, sizeof(destroy)); |
677 | + destroy.callback = (XIMProc)((XimClientData::XIMEndCallback)); |
678 | + destroy.client_data = (XPointer)data; |
679 | + XSetIMValues((data->m_xim), XNDestroyCallback, &destroy, NULL); |
680 | + XGetIMValues((data->m_xim), XNQueryInputStyle, &xim_styles, NULL); |
681 | + for (i=0; i<xim_styles->count_styles; i++) |
682 | + { |
683 | + if (preferred_style == xim_styles->supported_styles[i]) |
684 | + { |
685 | + choosed_style = preferred_style; |
686 | + break; |
687 | + } |
688 | + choosed_style = choose_better_style(choosed_style, |
689 | + xim_styles->supported_styles[i]); |
690 | + } |
691 | + |
692 | + if (choosed_style == 0) |
693 | + { |
694 | + for (i=0; i<xim_styles->count_styles; i++) |
695 | + { |
696 | + if (xim_styles->supported_styles[i] == root_style) |
697 | + { |
698 | + break; |
699 | + } |
700 | + } |
701 | + if (i>=xim_styles->count_styles) |
702 | + { |
703 | + nuxDebugMsg("[XimClientData::XIMStartCallback] root styles not supported."); |
704 | + return; |
705 | + } |
706 | + im_style = root_style; |
707 | + } |
708 | + |
709 | + if ((choosed_style & PREEDIT_MASK) == XIMPreeditCallbacks) |
710 | + { |
711 | + XIMCallback preedit_start; |
712 | + XIMCallback preedit_done; |
713 | + XIMCallback preedit_draw; |
714 | + XIMCallback preedit_caret; |
715 | + |
716 | + preedit_start.callback = (XIMProc)((XimClientData::XIMPreeditStartCallback)); |
717 | + preedit_start.client_data = (XPointer)data; |
718 | + preedit_done.callback = (XIMProc)((XimClientData::XIMPreeditDoneCallback)); |
719 | + preedit_done.client_data = (XPointer)data; |
720 | + preedit_draw.callback = (XIMProc)((XimClientData::XIMPreeditDrawCallback)); |
721 | + preedit_draw.client_data = (XPointer)data; |
722 | + preedit_caret.callback = (XIMProc)((XimClientData::XIMPreeditCaretCallback)); |
723 | + preedit_caret.client_data = (XPointer)data; |
724 | + |
725 | + name1 = XNPreeditAttributes; |
726 | + list1 = XVaCreateNestedList(0, |
727 | + XNPreeditStartCallback, &preedit_start, |
728 | + XNPreeditDoneCallback, &preedit_done, |
729 | + XNPreeditDrawCallback, &preedit_draw, |
730 | + XNPreeditCaretCallback, &preedit_caret, |
731 | + NULL); |
732 | + |
733 | + im_style |= XIMPreeditCallbacks; |
734 | + } |
735 | + else if ((choosed_style & PREEDIT_MASK) == XIMPreeditNone) |
736 | + { |
737 | + im_style |= XIMPreeditNone; |
738 | + } |
739 | + else |
740 | + { |
741 | + im_style |= XIMPreeditNothing; |
742 | + } |
743 | + |
744 | +#if 0 |
745 | + if ((choosed_style & STATUS_MASK) == XIMStatusCallbacks) |
746 | + { |
747 | + XIMCallback status_start; |
748 | + XIMCallback status_done; |
749 | + XIMCallback status_draw; |
750 | + |
751 | + status_start.callback = (XIMProc)((XimClientData::XIMStatusStartCallback)); |
752 | + status_start.client_data = (XPointer)data; |
753 | + status_done.callback = (XIMProc)((XimClientData::XIMStatusDoneCallback)); |
754 | + status_done.client_data = (XPointer)data; |
755 | + status_draw.callback = (XIMProc)((XimClientData::XIMStatusDrawCallback)); |
756 | + status_draw.client_data = (XPointer)data; |
757 | + |
758 | + name2 = XNStatusAttributes; |
759 | + list2 = XVaCreateNestedList(0, |
760 | + XNStatusStartCallback, &status_start, |
761 | + XNStatusDoneCallback, &status_done, |
762 | + XNStatusDrawCallback, &status_draw, |
763 | + NULL); |
764 | + |
765 | + im_style |= XIMStatusCallbacks; |
766 | + } |
767 | + else if ((choosed_style & STATUS_MASK) == XIMStatusNone) |
768 | + { |
769 | + im_style |= XIMStatusNone; |
770 | + } |
771 | + else |
772 | + { |
773 | + im_style |= XIMStatusNothing; |
774 | + } |
775 | +#endif |
776 | + |
777 | + if (name2 && !name1) |
778 | + { |
779 | + name1 = name2; |
780 | + list1 = list2; |
781 | + name2 = NULL; |
782 | + list2 = NULL; |
783 | + } |
784 | + |
785 | + data->m_xic = XCreateIC(data->m_xim, |
786 | + XNInputStyle, im_style, |
787 | + XNClientWindow, win, |
788 | + XNFocusWindow, win, |
789 | + name1, list1, |
790 | + name2, list2, |
791 | + NULL); |
792 | + if (!(data->m_xic)) |
793 | + { |
794 | + nuxDebugMsg("[XimClientData::XIMStartCallback] failed to register xic"); |
795 | + } |
796 | + |
797 | + /* TODO: string conversion support */ |
798 | + |
799 | + if (list1) |
800 | + XFree(list1); |
801 | + if (list2) |
802 | + XFree(list2); |
803 | + |
804 | + return; |
805 | + } |
806 | + |
807 | + int XimClientData::XIMPreeditStartCallback(XIC xic, XPointer client_data, XPointer call_data) |
808 | + { |
809 | + return -1; /* No Length Limit */ |
810 | + } |
811 | + |
812 | + void XimClientData::XIMPreeditDoneCallback(XIC xic, XPointer client_data, XPointer call_data) |
813 | + { |
814 | + struct XimClientData *data = (struct XimClientData*)client_data; |
815 | + |
816 | + if (data->textentry == NULL) |
817 | + return; |
818 | + |
819 | + data->textentry->ResetPreedit(); |
820 | + /* FIXME: If the preedit is accepted (press Return for example) then this |
821 | + * QueueRefresh() will lead to a visiable blink in the entry. |
822 | + * However without this QueueRefresh() then the display will not be |
823 | + * refreshed if the preedit is cancelled (press Esc for example). |
824 | + */ |
825 | + data->textentry->QueueRefresh(true, true); |
826 | + } |
827 | + |
828 | + void XimClientData::XIMPreeditDrawCallback(XIC xic, XPointer client_data, |
829 | + XIMPreeditDrawCallbackStruct *call_data) |
830 | + { |
831 | + struct XimClientData *data = (struct XimClientData*)client_data; |
832 | + |
833 | + if (data->textentry == NULL) |
834 | + return; |
835 | + |
836 | + if (call_data->text) |
837 | + { |
838 | + std::string &preedit = data->textentry->preedit_; |
839 | + if (call_data->text->encoding_is_wchar) |
840 | + preedit = NString(call_data->text->string.wide_char).GetTStringRef(); |
841 | + else |
842 | + preedit = call_data->text->string.multi_byte; |
843 | + |
844 | + const char *utf8 = preedit.c_str(); |
845 | + PangoAttrList *attrs; |
846 | + feedbacks_to_pango_attr_list(&attrs, utf8, |
847 | + call_data->text->length, call_data->text->feedback); |
848 | + |
849 | + if (data->textentry->preedit_attrs_) |
850 | + pango_attr_list_unref(data->textentry->preedit_attrs_); |
851 | + |
852 | + data->textentry->preedit_cursor_ = preedit.length(); |
853 | + data->textentry->preedit_attrs_ = attrs; |
854 | + data->textentry->QueueRefresh(true, true); |
855 | + } |
856 | + } |
857 | + |
858 | + void XimClientData::XIMPreeditCaretCallback(XIC xic, XPointer client_data, |
859 | + XIMPreeditCaretCallbackStruct *call_data) |
860 | + { |
861 | + nuxDebugMsg("[XimClientData::XIMPreeditCaretCallback] TODO"); |
862 | + } |
863 | + |
864 | + void XimClientData::XIMStatusStartCallback(XIC xic, XPointer client_data, XPointer call_data) |
865 | + { |
866 | + nuxDebugMsg("[XimClientData::XIMStatusStartCallback] TODO"); |
867 | + } |
868 | + |
869 | + void XimClientData::XIMStatusDoneCallback(XIC xic, XPointer client_data, XPointer call_data) |
870 | + { |
871 | + nuxDebugMsg("[XimClientData::XIMStatusDoneCallback] TODO"); |
872 | + } |
873 | + |
874 | + void XimClientData::XIMStatusDrawCallback(XIC xic, XPointer client_data, |
875 | + XIMStatusDrawCallbackStruct *call_data) |
876 | + { |
877 | + nuxDebugMsg("[XimClientData::XIMStatusDrawCallback] TODO"); |
878 | + } |
879 | + |
880 | + void XimClientData::XIMStringConversionCallback(XIC xic, XPointer client_data, |
881 | + XIMStringConversionCallbackStruct * call_data) |
882 | + { |
883 | + nuxDebugMsg("[XimClientData::XIMStringConversionCallback] TODO"); |
884 | + } |
885 | +} |
886 | |
887 | === added file 'Nux/XimClientData.h' |
888 | --- Nux/XimClientData.h 1970-01-01 00:00:00 +0000 |
889 | +++ Nux/XimClientData.h 2012-07-20 05:33:28 +0000 |
890 | @@ -0,0 +1,64 @@ |
891 | +/* |
892 | +* Copyright 2010 Inalogic庐 Inc. |
893 | +* |
894 | +* This program is free software: you can redistribute it and/or modify it |
895 | +* under the terms of the GNU Lesser General Public License, as |
896 | +* published by the Free Software Foundation; either version 2.1 or 3.0 |
897 | +* of the License. |
898 | +* |
899 | +* This program is distributed in the hope that it will be useful, but |
900 | +* WITHOUT ANY WARRANTY; without even the implied warranties of |
901 | +* MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR |
902 | +* PURPOSE. See the applicable version of the GNU Lesser General Public |
903 | +* License for more details. |
904 | +* |
905 | +* You should have received a copy of both the GNU Lesser General Public |
906 | +* License along with this program. If not, see <http://www.gnu.org/licenses/> |
907 | +* |
908 | +* Authored by: Ying-Chun Liu <paul.liu@canonical.com> |
909 | +* Yu Ning <yuningdodo@gmail.com> |
910 | +* |
911 | +*/ |
912 | + |
913 | + |
914 | +#ifndef XIMCLIENTDATA_H |
915 | +#define XIMCLIENTDATA_H |
916 | + |
917 | +#include <Nux/Nux.h> |
918 | + |
919 | +namespace nux |
920 | +{ |
921 | + |
922 | + class TextEntry; |
923 | + |
924 | + struct XimClientData |
925 | + { |
926 | + XIM m_xim; |
927 | + XIC m_xic; |
928 | + Window window; |
929 | + Display *display; |
930 | + int focus_stat; |
931 | + TextEntry *textentry; |
932 | + |
933 | + void InitXIM(); |
934 | + void UninitXIM(); |
935 | + void Reset(); |
936 | + void XICFocus(TextEntry *textentry); |
937 | + void XICUnFocus(TextEntry *textentry); |
938 | + void SetCursorLocation(int x, int y, int width, int height); |
939 | + |
940 | + static void XIMEndCallback(Display *dpy, XPointer client_data, XPointer call_data); |
941 | + static void XIMStartCallback(Display *dpy, XPointer client_data, XPointer call_data); |
942 | + static int XIMPreeditStartCallback(XIC xic, XPointer client_data, XPointer call_data); |
943 | + static void XIMPreeditDoneCallback(XIC xic, XPointer client_data, XPointer call_data); |
944 | + static void XIMPreeditDrawCallback(XIC xic, XPointer client_data, XIMPreeditDrawCallbackStruct *call_data); |
945 | + static void XIMPreeditCaretCallback(XIC xic, XPointer client_data, XIMPreeditCaretCallbackStruct *call_data); |
946 | + static void XIMStatusStartCallback(XIC xic, XPointer client_data, XPointer call_data); |
947 | + static void XIMStatusDoneCallback(XIC xic, XPointer client_data, XPointer call_data); |
948 | + static void XIMStatusDrawCallback(XIC xic, XPointer client_data, XIMStatusDrawCallbackStruct *call_data); |
949 | + static void XIMStringConversionCallback(XIC xic, XPointer client_data, XIMStringConversionCallbackStruct * call_data); |
950 | + }; |
951 | + |
952 | +} |
953 | +#endif // XIMCLIENTDATA_H |
954 | + |
955 | |
956 | === modified file 'NuxGraphics/GraphicsDisplayX11.h' |
957 | --- NuxGraphics/GraphicsDisplayX11.h 2012-01-26 23:01:57 +0000 |
958 | +++ NuxGraphics/GraphicsDisplayX11.h 2012-07-20 05:33:28 +0000 |
959 | @@ -462,6 +462,7 @@ |
960 | |
961 | friend class DisplayAccessController; |
962 | friend class GraphicsEngine; |
963 | + friend class XimIMEContext; |
964 | }; |
965 | |
966 | } |
Well, I just pushed a new version to my branch, you can take a look at it, I change the InputMethod to a global one instead of "per" text entry, this is the idea used by most of the toolkit including gtk and qt. And it would be more easy to implement XIM support (Since XIM is global).
And one hint, Nux provide a x11Event filter, thus when InputMethodXim constructs, it can register a new filter and call "XFilterEvent" in the registered filter.
And so we can move all function into InputMethodXim, ProcessKeyEvent can simple return false for InputMethodXim, that's Ok.
As for fcitx support, we should move all separte support outside (including XIM, ibus), it's not hard to do this, the things need to be done before we move it out are
1. implementing xim support better,
2. remove the "friend class", and expose more interface for TextEntry.