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 ;
7
5
8
6
use super :: { ffi, util, XConnection , XError } ;
9
7
use crate :: platform_impl:: platform:: x11:: ime:: { ImeEvent , ImeEventSender } ;
10
8
use std:: ffi:: CStr ;
11
9
use x11_dl:: xlib:: { XIMCallback , XIMPreeditCaretCallbackStruct , XIMPreeditDrawCallbackStruct } ;
12
10
11
+ /// IME creation error.
13
12
#[ derive( Debug ) ]
14
13
pub enum ImeContextCreationError {
14
+ /// Got the error from Xlib.
15
15
XError ( XError ) ,
16
+
17
+ /// Got null pointer from Xlib but without exact reason.
16
18
Null ,
17
19
}
20
+
21
+ /// The callback used by XIM preedit functions.
18
22
type 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.
19
34
extern "C" fn preedit_start_callback (
20
35
_xim : ffi:: XIM ,
21
36
client_data : ffi:: XPointer ,
@@ -32,6 +47,8 @@ extern "C" fn preedit_start_callback(
32
47
-1
33
48
}
34
49
50
+ /// Done callback is called when the user finished preedit and the text is about to be inserted into
51
+ /// underlying widget.
35
52
extern "C" fn preedit_done_callback (
36
53
_xim : ffi:: XIM ,
37
54
client_data : ffi:: XPointer ,
@@ -53,6 +70,7 @@ fn calc_byte_position(text: &Vec<char>, pos: usize) -> usize {
53
70
byte_pos
54
71
}
55
72
73
+ /// Preedit text information to be drawn inline by the client.
56
74
extern "C" fn preedit_draw_callback (
57
75
_xim : ffi:: XIM ,
58
76
client_data : ffi:: XPointer ,
@@ -103,6 +121,7 @@ extern "C" fn preedit_draw_callback(
103
121
. expect ( "failed to send preedit update event" ) ;
104
122
}
105
123
124
+ /// Handling of cursor movements in preedit text.
106
125
extern "C" fn preedit_caret_callback (
107
126
_xim : ffi:: XIM ,
108
127
client_data : ffi:: XPointer ,
@@ -122,65 +141,12 @@ extern "C" fn preedit_caret_callback(
122
141
. expect ( "failed to send preedit update event" ) ;
123
142
}
124
143
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 ,
184
150
}
185
151
186
152
impl PreeditCallbacks {
@@ -201,22 +167,23 @@ impl PreeditCallbacks {
201
167
}
202
168
}
203
169
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 ,
209
175
}
210
176
211
- // WARNING : this struct doesn't destroy its XIC resource when dropped.
177
+ // XXX : this struct doesn't destroy its XIC resource when dropped.
212
178
// This is intentional, as it doesn't have enough information to know whether or not the context
213
179
// still exists on the server. Since `ImeInner` has that awareness, destruction must be handled
214
180
// through `ImeInner`.
215
181
pub 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 > ,
220
187
}
221
188
222
189
impl ImeContext {
@@ -227,80 +194,86 @@ impl ImeContext {
227
194
ic_spot : Option < ffi:: XPoint > ,
228
195
event_sender : ImeEventSender ,
229
196
) -> Result < Self , ImeContextCreationError > {
230
- let client_data = Box :: new ( ImeContextClientData {
197
+ let client_data = Box :: into_raw ( Box :: new ( ImeContextClientData {
231
198
window,
232
199
event_sender,
233
200
text : Vec :: new ( ) ,
234
201
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 ) ?;
243
206
244
- let ic = ic. ok_or ( ImeContextCreationError :: Null ) ?;
245
207
xconn
246
208
. check_errors ( )
247
209
. map_err ( ImeContextCreationError :: XError ) ?;
248
210
249
- Ok ( ImeContext {
211
+ let mut context = ImeContext {
250
212
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
+ } ;
256
216
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 )
278
220
}
221
+
222
+ Ok ( context)
279
223
}
280
224
281
- unsafe fn create_ic_with_spot (
225
+ unsafe fn create_ic (
282
226
xconn : & Arc < XConnection > ,
283
227
im : ffi:: XIM ,
284
228
window : ffi:: Window ,
285
- ic_spot : ffi:: XPoint ,
286
- preedit_callbacks : & PreeditCallbacks ,
229
+ client_data : ffi:: XPointer ,
287
230
) -> 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)
304
277
}
305
278
306
279
pub fn focus ( & self , xconn : & Arc < XConnection > ) -> Result < ( ) , XError > {
@@ -317,19 +290,34 @@ impl ImeContext {
317
290
xconn. check_errors ( )
318
291
}
319
292
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.
320
298
pub fn set_spot ( & mut self , xconn : & Arc < XConnection > , x : c_short , y : c_short ) {
321
299
if self . ic_spot . x == x && self . ic_spot . y == y {
322
300
return ;
323
301
}
302
+
324
303
self . ic_spot = ffi:: XPoint { x, y } ;
325
304
326
305
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
+
329
317
( xconn. xlib . XSetICValues ) (
330
318
self . ic ,
331
319
ffi:: XNPreeditAttributes_0 . as_ptr ( ) as * const _ ,
332
- pre_edit_attr . ptr ,
320
+ preedit_attr . ptr ,
333
321
ptr:: null_mut :: < ( ) > ( ) ,
334
322
) ;
335
323
}
0 commit comments