1- use std:: {
2- mem:: transmute,
3- os:: raw:: { c_short, c_void} ,
4- ptr,
5- sync:: Arc ,
6- } ;
1+ use std:: mem:: transmute;
2+ use std:: os:: raw:: c_short;
3+ use std:: ptr;
4+ use std:: sync:: Arc ;
75
86use super :: { ffi, util, XConnection , XError } ;
97use crate :: platform_impl:: platform:: x11:: ime:: { ImeEvent , ImeEventSender } ;
108use std:: ffi:: CStr ;
119use x11_dl:: xlib:: { XIMCallback , XIMPreeditCaretCallbackStruct , XIMPreeditDrawCallbackStruct } ;
1210
11+ /// IME creation error.
1312#[ derive( Debug ) ]
1413pub enum ImeContextCreationError {
14+ /// Got the error from Xlib.
1515 XError ( XError ) ,
16+
17+ /// Got null pointer from Xlib but without exact reason.
1618 Null ,
1719}
20+
21+ /// The callback used by XIM preedit functions.
1822type XIMProcNonnull = unsafe extern "C" fn ( ffi:: XIM , ffi:: XPointer , ffi:: XPointer ) ;
23+
24+ /// Wrapper for creating XIM callbacks.
25+ #[ inline]
26+ fn create_xim_callback ( client_data : ffi:: XPointer , callback : XIMProcNonnull ) -> ffi:: XIMCallback {
27+ XIMCallback {
28+ client_data,
29+ callback : Some ( callback) ,
30+ }
31+ }
32+
33+ /// The server started preedit.
1934extern "C" fn preedit_start_callback (
2035 _xim : ffi:: XIM ,
2136 client_data : ffi:: XPointer ,
@@ -32,6 +47,8 @@ extern "C" fn preedit_start_callback(
3247 -1
3348}
3449
50+ /// Done callback is called when the user finished preedit and the text is about to be inserted into
51+ /// underlying widget.
3552extern "C" fn preedit_done_callback (
3653 _xim : ffi:: XIM ,
3754 client_data : ffi:: XPointer ,
@@ -53,6 +70,7 @@ fn calc_byte_position(text: &Vec<char>, pos: usize) -> usize {
5370 byte_pos
5471}
5572
73+ /// Preedit text information to be drawn inline by the client.
5674extern "C" fn preedit_draw_callback (
5775 _xim : ffi:: XIM ,
5876 client_data : ffi:: XPointer ,
@@ -103,6 +121,7 @@ extern "C" fn preedit_draw_callback(
103121 . expect ( "failed to send preedit update event" ) ;
104122}
105123
124+ /// Handling of cursor movements in preedit text.
106125extern "C" fn preedit_caret_callback (
107126 _xim : ffi:: XIM ,
108127 client_data : ffi:: XPointer ,
@@ -122,65 +141,12 @@ extern "C" fn preedit_caret_callback(
122141 . expect ( "failed to send preedit update event" ) ;
123142}
124143
125- unsafe fn create_pre_edit_attr < ' a > (
126- xconn : & ' a Arc < XConnection > ,
127- preedit_callbacks : & ' a PreeditCallbacks ,
128- ) -> util:: XSmartPointer < ' a , c_void > {
129- util:: XSmartPointer :: new (
130- xconn,
131- ( xconn. xlib . XVaCreateNestedList ) (
132- 0 ,
133- ffi:: XNPreeditStartCallback_0 . as_ptr ( ) as * const _ ,
134- & ( preedit_callbacks. start_callback ) as * const _ ,
135- ffi:: XNPreeditDoneCallback_0 . as_ptr ( ) as * const _ ,
136- & ( preedit_callbacks. done_callback ) as * const _ ,
137- ffi:: XNPreeditCaretCallback_0 . as_ptr ( ) as * const _ ,
138- & ( preedit_callbacks. caret_callback ) as * const _ ,
139- ffi:: XNPreeditDrawCallback_0 . as_ptr ( ) as * const _ ,
140- & ( preedit_callbacks. draw_callback ) as * const _ ,
141- ptr:: null_mut :: < ( ) > ( ) ,
142- ) ,
143- )
144- . expect ( "XVaCreateNestedList returned NULL" )
145- }
146-
147- unsafe fn create_pre_edit_attr_with_spot < ' a > (
148- xconn : & ' a Arc < XConnection > ,
149- ic_spot : & ' a ffi:: XPoint ,
150- preedit_callbacks : & ' a PreeditCallbacks ,
151- ) -> util:: XSmartPointer < ' a , c_void > {
152- util:: XSmartPointer :: new (
153- xconn,
154- ( xconn. xlib . XVaCreateNestedList ) (
155- 0 ,
156- ffi:: XNSpotLocation_0 . as_ptr ( ) as * const _ ,
157- ic_spot,
158- ffi:: XNPreeditStartCallback_0 . as_ptr ( ) as * const _ ,
159- & preedit_callbacks. start_callback as * const _ ,
160- ffi:: XNPreeditDoneCallback_0 . as_ptr ( ) as * const _ ,
161- & preedit_callbacks. done_callback as * const _ ,
162- ffi:: XNPreeditCaretCallback_0 . as_ptr ( ) as * const _ ,
163- & preedit_callbacks. caret_callback as * const _ ,
164- ffi:: XNPreeditDrawCallback_0 . as_ptr ( ) as * const _ ,
165- & preedit_callbacks. draw_callback as * const _ ,
166- ptr:: null_mut :: < ( ) > ( ) ,
167- ) ,
168- )
169- . expect ( "XVaCreateNestedList returned NULL" )
170- }
171-
172- fn create_xim_callback ( client_data : ffi:: XPointer , callback : XIMProcNonnull ) -> ffi:: XIMCallback {
173- XIMCallback {
174- client_data,
175- callback : Some ( callback) ,
176- }
177- }
178-
179- pub struct PreeditCallbacks {
180- pub start_callback : ffi:: XIMCallback ,
181- pub done_callback : ffi:: XIMCallback ,
182- pub draw_callback : ffi:: XIMCallback ,
183- pub caret_callback : ffi:: XIMCallback ,
144+ /// Struct to simplify callback creation and latter passing into Xlib XIM.
145+ struct PreeditCallbacks {
146+ start_callback : ffi:: XIMCallback ,
147+ done_callback : ffi:: XIMCallback ,
148+ draw_callback : ffi:: XIMCallback ,
149+ caret_callback : ffi:: XIMCallback ,
184150}
185151
186152impl PreeditCallbacks {
@@ -201,22 +167,23 @@ impl PreeditCallbacks {
201167 }
202168}
203169
204- pub struct ImeContextClientData {
205- pub window : ffi:: Window ,
206- pub event_sender : ImeEventSender ,
207- pub text : Vec < char > ,
208- pub cursor_pos : usize ,
170+ struct ImeContextClientData {
171+ window : ffi:: Window ,
172+ event_sender : ImeEventSender ,
173+ text : Vec < char > ,
174+ cursor_pos : usize ,
209175}
210176
211- // WARNING : this struct doesn't destroy its XIC resource when dropped.
177+ // XXX : this struct doesn't destroy its XIC resource when dropped.
212178// This is intentional, as it doesn't have enough information to know whether or not the context
213179// still exists on the server. Since `ImeInner` has that awareness, destruction must be handled
214180// through `ImeInner`.
215181pub struct ImeContext {
216- pub ic : ffi:: XIC ,
217- pub ic_spot : ffi:: XPoint ,
218- pub preedit_callbacks : PreeditCallbacks ,
219- pub client_data : Box < ImeContextClientData > ,
182+ pub ( super ) ic : ffi:: XIC ,
183+ pub ( super ) ic_spot : ffi:: XPoint ,
184+ // Since the data is passed shared between X11 XIM callbacks, but couldn't be direclty free from
185+ // there we keep the pointer to automatically deallocate it.
186+ _client_data : Box < ImeContextClientData > ,
220187}
221188
222189impl ImeContext {
@@ -227,80 +194,86 @@ impl ImeContext {
227194 ic_spot : Option < ffi:: XPoint > ,
228195 event_sender : ImeEventSender ,
229196 ) -> Result < Self , ImeContextCreationError > {
230- let client_data = Box :: new ( ImeContextClientData {
197+ let client_data = Box :: into_raw ( Box :: new ( ImeContextClientData {
231198 window,
232199 event_sender,
233200 text : Vec :: new ( ) ,
234201 cursor_pos : 0 ,
235- } ) ;
236- let client_data_ptr = Box :: into_raw ( client_data) ;
237- let preedit_callbacks = PreeditCallbacks :: new ( client_data_ptr as ffi:: XPointer ) ;
238- let ic = if let Some ( ic_spot) = ic_spot {
239- ImeContext :: create_ic_with_spot ( xconn, im, window, ic_spot, & preedit_callbacks)
240- } else {
241- ImeContext :: create_ic ( xconn, im, window, & preedit_callbacks)
242- } ;
202+ } ) ) ;
203+
204+ let ic = ImeContext :: create_ic ( xconn, im, window, client_data as ffi:: XPointer )
205+ . ok_or ( ImeContextCreationError :: Null ) ?;
243206
244- let ic = ic. ok_or ( ImeContextCreationError :: Null ) ?;
245207 xconn
246208 . check_errors ( )
247209 . map_err ( ImeContextCreationError :: XError ) ?;
248210
249- Ok ( ImeContext {
211+ let mut context = ImeContext {
250212 ic,
251- ic_spot : ic_spot. unwrap_or ( ffi:: XPoint { x : 0 , y : 0 } ) ,
252- preedit_callbacks,
253- client_data : Box :: from_raw ( client_data_ptr) ,
254- } )
255- }
213+ ic_spot : ffi:: XPoint { x : 0 , y : 0 } ,
214+ _client_data : Box :: from_raw ( client_data) ,
215+ } ;
256216
257- unsafe fn create_ic (
258- xconn : & Arc < XConnection > ,
259- im : ffi:: XIM ,
260- window : ffi:: Window ,
261- preedit_callbacks : & PreeditCallbacks ,
262- ) -> Option < ffi:: XIC > {
263- let pre_edit_attr = create_pre_edit_attr ( xconn, preedit_callbacks) ;
264- let ic = ( xconn. xlib . XCreateIC ) (
265- im,
266- ffi:: XNInputStyle_0 . as_ptr ( ) as * const _ ,
267- ffi:: XIMPreeditCallbacks | ffi:: XIMStatusNothing ,
268- ffi:: XNClientWindow_0 . as_ptr ( ) as * const _ ,
269- window,
270- ffi:: XNPreeditAttributes_0 . as_ptr ( ) ,
271- pre_edit_attr. ptr ,
272- ptr:: null_mut :: < ( ) > ( ) ,
273- ) ;
274- if ic. is_null ( ) {
275- None
276- } else {
277- Some ( ic)
217+ // Set the spot location, if it's present.
218+ if let Some ( ic_spot) = ic_spot {
219+ context. set_spot ( xconn, ic_spot. x , ic_spot. y )
278220 }
221+
222+ Ok ( context)
279223 }
280224
281- unsafe fn create_ic_with_spot (
225+ unsafe fn create_ic (
282226 xconn : & Arc < XConnection > ,
283227 im : ffi:: XIM ,
284228 window : ffi:: Window ,
285- ic_spot : ffi:: XPoint ,
286- preedit_callbacks : & PreeditCallbacks ,
229+ client_data : ffi:: XPointer ,
287230 ) -> Option < ffi:: XIC > {
288- let pre_edit_attr = create_pre_edit_attr_with_spot ( xconn, & ic_spot, preedit_callbacks) ;
289- let ic = ( xconn. xlib . XCreateIC ) (
290- im,
291- ffi:: XNInputStyle_0 . as_ptr ( ) as * const _ ,
292- ffi:: XIMPreeditCallbacks | ffi:: XIMStatusNothing ,
293- ffi:: XNClientWindow_0 . as_ptr ( ) as * const _ ,
294- window,
295- ffi:: XNPreeditAttributes_0 . as_ptr ( ) as * const _ ,
296- pre_edit_attr. ptr ,
297- ptr:: null_mut :: < ( ) > ( ) ,
298- ) ;
299- if ic. is_null ( ) {
300- None
301- } else {
302- Some ( ic)
303- }
231+ let preedit_callbacks = PreeditCallbacks :: new ( client_data) ;
232+ let preedit_attr = util:: XSmartPointer :: new (
233+ xconn,
234+ ( xconn. xlib . XVaCreateNestedList ) (
235+ 0 ,
236+ ffi:: XNPreeditStartCallback_0 . as_ptr ( ) as * const _ ,
237+ & ( preedit_callbacks. start_callback ) as * const _ ,
238+ ffi:: XNPreeditDoneCallback_0 . as_ptr ( ) as * const _ ,
239+ & ( preedit_callbacks. done_callback ) as * const _ ,
240+ ffi:: XNPreeditCaretCallback_0 . as_ptr ( ) as * const _ ,
241+ & ( preedit_callbacks. caret_callback ) as * const _ ,
242+ ffi:: XNPreeditDrawCallback_0 . as_ptr ( ) as * const _ ,
243+ & ( preedit_callbacks. draw_callback ) as * const _ ,
244+ ptr:: null_mut :: < ( ) > ( ) ,
245+ ) ,
246+ )
247+ . expect ( "XVaCreateNestedList returned NULL" ) ;
248+
249+ let ic = {
250+ let ic = ( xconn. xlib . XCreateIC ) (
251+ im,
252+ ffi:: XNInputStyle_0 . as_ptr ( ) as * const _ ,
253+ ffi:: XIMPreeditCallbacks | ffi:: XIMStatusNothing ,
254+ ffi:: XNClientWindow_0 . as_ptr ( ) as * const _ ,
255+ window,
256+ ffi:: XNPreeditAttributes_0 . as_ptr ( ) as * const _ ,
257+ preedit_attr. ptr ,
258+ ptr:: null_mut :: < ( ) > ( ) ,
259+ ) ;
260+
261+ // If we've failed to create IC with preedit callbacks fallback to normal one.
262+ if ic. is_null ( ) {
263+ ( xconn. xlib . XCreateIC ) (
264+ im,
265+ ffi:: XNInputStyle_0 . as_ptr ( ) as * const _ ,
266+ ffi:: XIMPreeditNothing | ffi:: XIMStatusNothing ,
267+ ffi:: XNClientWindow_0 . as_ptr ( ) as * const _ ,
268+ window,
269+ ptr:: null_mut :: < ( ) > ( ) ,
270+ )
271+ } else {
272+ ic
273+ }
274+ } ;
275+
276+ ( !ic. is_null ( ) ) . then ( || ic)
304277 }
305278
306279 pub fn focus ( & self , xconn : & Arc < XConnection > ) -> Result < ( ) , XError > {
@@ -317,19 +290,34 @@ impl ImeContext {
317290 xconn. check_errors ( )
318291 }
319292
293+ // Set the spot for preedit text. Setting spot isn't working with libX11 when preedit callbacks
294+ // are being used. Certain IMEs do show selection window, but it's placed in bottom left of the
295+ // window and couldn't be changed.
296+ //
297+ // For me see: https://bugs.freedesktop.org/show_bug.cgi?id=1580.
320298 pub fn set_spot ( & mut self , xconn : & Arc < XConnection > , x : c_short , y : c_short ) {
321299 if self . ic_spot . x == x && self . ic_spot . y == y {
322300 return ;
323301 }
302+
324303 self . ic_spot = ffi:: XPoint { x, y } ;
325304
326305 unsafe {
327- let pre_edit_attr =
328- create_pre_edit_attr_with_spot ( xconn, & self . ic_spot , & self . preedit_callbacks ) ;
306+ let preedit_attr = util:: XSmartPointer :: new (
307+ xconn,
308+ ( xconn. xlib . XVaCreateNestedList ) (
309+ 0 ,
310+ ffi:: XNSpotLocation_0 . as_ptr ( ) ,
311+ & self . ic_spot ,
312+ ptr:: null_mut :: < ( ) > ( ) ,
313+ ) ,
314+ )
315+ . expect ( "XVaCreateNestedList returned NULL" ) ;
316+
329317 ( xconn. xlib . XSetICValues ) (
330318 self . ic ,
331319 ffi:: XNPreeditAttributes_0 . as_ptr ( ) as * const _ ,
332- pre_edit_attr . ptr ,
320+ preedit_attr . ptr ,
333321 ptr:: null_mut :: < ( ) > ( ) ,
334322 ) ;
335323 }
0 commit comments