1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Robert TIMM (rti) <mail@rtti.de>
6 // Copyright (C) 2013-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "stdopengl.h"
22 #include "unix_event_emitter.h"
24 #if defined(NL_OS_UNIX) && !defined(NL_OS_MAC)
27 #include <X11/Xatom.h>
28 #include <X11/keysym.h>
29 #include <X11/Xutil.h>
30 #include <X11/XKBlib.h>
31 #include "nel/misc/debug.h"
32 #include "nel/misc/utf_string_view.h"
38 typedef bool (*x11Proc
)(NL3D::IDriver
*drv
, XEvent
*e
);
40 static Atom XA_CLIPBOARD
= 0;
41 static Atom XA_UTF8_STRING
= 0;
42 static Atom XA_TARGETS
= 0;
43 static Atom XA_NEL_SEL
= 0;
44 static Atom XA_WM_DELETE_WINDOW
= 0;
48 CUnixEventEmitter::CUnixEventEmitter ():_dpy(NULL
), _win(0), _im(NULL
), _ic(NULL
), _driver(NULL
)
50 _SelectionOwned
= false;
53 CUnixEventEmitter::~CUnixEventEmitter()
58 void CUnixEventEmitter::init(Display
*dpy
, Window win
, NL3D::IDriver
*driver
)
64 XSelectInput (_dpy
, _win
, KeyPressMask
|KeyReleaseMask
|ButtonPressMask
|ButtonReleaseMask
|PointerMotionMask
|StructureNotifyMask
|ExposureMask
|EnterWindowMask
|LeaveWindowMask
|FocusChangeMask
);
66 // define Atoms used by clipboard
67 XA_CLIPBOARD
= XInternAtom(dpy
, "CLIPBOARD", False
);
68 XA_UTF8_STRING
= XInternAtom(dpy
, "UTF8_STRING", False
);
69 XA_TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
70 XA_NEL_SEL
= XInternAtom(dpy
, "NeL_SEL", False
);
72 // define Atom used by delete window
73 XA_WM_DELETE_WINDOW
= XInternAtom(dpy
, "WM_DELETE_WINDOW", False
);
74 XSetWMProtocols(dpy
, win
, &XA_WM_DELETE_WINDOW
, 1);
77 TODO: implements all useful events processing
78 ButtonMotionMask|Button1MotionMask|Button2MotionMask|
79 Button3MotionMask|Button4MotionMask|Button5MotionMask|KeymapStateMask|
80 SubstructureNotifyMask|VisibilityChangeMask|PropertyChangeMask|
81 ColormapChangeMask|OwnerGrabButtonMask
87 void CUnixEventEmitter::createIM()
89 #ifdef X_HAVE_UTF8_STRING
91 _im
= XOpenIM(_dpy
, NULL
, NULL
, NULL
);
95 XSetLocaleModifiers("@im=local");
97 _im
= XOpenIM(_dpy
, NULL
, NULL
, NULL
);
101 XSetLocaleModifiers("@im=");
103 _im
= XOpenIM(_dpy
, NULL
, NULL
, NULL
);
107 nlwarning("XOpenIM failed");
114 _ic
= XCreateIC(_im
, XNInputStyle
, XIMPreeditNothing
| XIMStatusNothing
, XNClientWindow
, _win
, XNFocusWindow
, _win
, NULL
);
118 nlwarning("XCreateIC failed");
124 void CUnixEventEmitter::closeIM()
126 #ifdef X_HAVE_UTF8_STRING
141 void CUnixEventEmitter::submitEvents(CEventServer
& server
, bool allWindows
)
143 while (XPending(_dpy
))
146 XNextEvent(_dpy
, &Event
);
147 if (allWindows
|| Event
.xany
.window
== _win
)
149 // nlinfo("event: %d", Event.type);
152 // forward X events to OpenGL driver
153 x11Proc proc
= (x11Proc
)_driver
->getWindowProc();
156 proc(_driver
, &Event
);
160 processMessage (Event
, &server
);
165 // Dispatch sent messages
166 _InternalServer
.setServer (&server
);
167 _InternalServer
.pump (allWindows
);
170 static Bool
isMouseMoveEvent(Display
*display
, XEvent
*event
, XPointer arg
)
172 return (event
->type
== MotionNotify
);
176 # define AltMask (Mod1Mask)
179 TMouseButton
getMouseButton (uint32 state
)
181 uint32 button
=noButton
;
182 if (state
&Button1Mask
) button
|=leftButton
;
183 if (state
&Button2Mask
) button
|=middleButton
;
184 if (state
&Button3Mask
) button
|=rightButton
;
185 if (state
&ControlMask
) button
|=ctrlButton
;
186 if (state
&ShiftMask
) button
|=shiftButton
;
187 if (state
&AltMask
) button
|=altButton
;
189 return (TMouseButton
)button
;
192 TKeyButton
getKeyButton (uint32 state
)
194 uint32 button
=noKeyButton
;
195 if (state
&ControlMask
) button
|=ctrlKeyButton
;
196 if (state
&ShiftMask
) button
|=shiftKeyButton
;
197 if (state
&AltMask
) button
|=altKeyButton
;
199 return (TKeyButton
)button
;
202 TKey
getKeyFromKeycode (uint keycode
)
204 // keycodes are depending on system
207 case 0x0a: return Key1
;
208 case 0x0b: return Key2
;
209 case 0x0c: return Key3
;
210 case 0x0d: return Key4
;
211 case 0x0e: return Key5
;
212 case 0x0f: return Key6
;
213 case 0x10: return Key7
;
214 case 0x11: return Key8
;
215 case 0x12: return Key9
;
216 case 0x13: return Key0
;
217 case 0x14: return KeyLBRACKET
;
218 case 0x15: return KeyEQUALS
;
219 case 0x22: return KeyRBRACKET
;
220 case 0x23: return KeySEMICOLON
;
221 case 0x2f: return KeyCOMMA
;
222 case 0x30: return KeyTILDE
;
223 case 0x31: return KeyAPOSTROPHE
;
224 case 0x33: return KeyBACKSLASH
;
225 case 0x5e: return KeyOEM_102
;
226 // case 0x3a: return KeyCOMMA;
227 case 0x3b: return KeyPERIOD
;
228 case 0x3c: return KeySLASH
;
229 case 0x3d: return KeyPARAGRAPH
;
230 // for non-standard keyboards, maps to QWERTY keys
231 case 0x18: return KeyQ
;
232 case 0x19: return KeyW
;
233 case 0x1a: return KeyE
;
234 case 0x1b: return KeyR
;
235 case 0x1c: return KeyT
;
236 case 0x1d: return KeyY
;
237 case 0x1e: return KeyU
;
238 case 0x1f: return KeyI
;
239 case 0x20: return KeyO
;
240 case 0x21: return KeyP
;
241 case 0x26: return KeyQ
;
242 case 0x27: return KeyS
;
243 case 0x28: return KeyD
;
244 case 0x29: return KeyF
;
245 case 0x2a: return KeyG
;
246 case 0x2b: return KeyH
;
247 case 0x2c: return KeyJ
;
248 case 0x2d: return KeyK
;
249 case 0x2e: return KeyL
;
250 case 0x34: return KeyZ
;
251 case 0x35: return KeyX
;
252 case 0x36: return KeyC
;
253 case 0x37: return KeyV
;
254 case 0x38: return KeyB
;
255 case 0x39: return KeyN
;
256 case 0x3a: return KeyM
;
258 // nlwarning("missing keycode 0x%x %d '%c'", keycode, keycode, keycode);
265 TKey
getKeyFromKeySym (KeySym keysym
)
267 // nlwarning("0x%x %d '%c'", keysym, keysym, keysym);
270 case XK_BackSpace
: return KeyBACK
;
271 case XK_Tab
: return KeyTAB
;
272 case XK_Return
: return KeyRETURN
;
273 case XK_Sys_Req
: return KeySNAPSHOT
;
274 case XK_Scroll_Lock
: return KeySCROLL
;
275 case XK_Pause
: return KeyPAUSE
;
276 case XK_Escape
: return KeyESCAPE
;
277 case XK_Delete
: return KeyDELETE
;
278 case XK_Home
: return KeyHOME
;
279 case XK_Left
: return KeyLEFT
;
280 case XK_Up
: return KeyUP
;
281 case XK_Right
: return KeyRIGHT
;
282 case XK_Down
: return KeyDOWN
;
283 case XK_Page_Up
: return KeyPRIOR
;
284 case XK_Page_Down
: return KeyNEXT
;
285 case XK_End
: return KeyEND
;
286 case XK_Print
: return KeyPRINT
;
287 case XK_Insert
: return KeyINSERT
;
288 case XK_Num_Lock
: return KeyNUMLOCK
;
289 case XK_KP_0
: return KeyNUMPAD0
;
290 case XK_KP_1
: return KeyNUMPAD1
;
291 case XK_KP_2
: return KeyNUMPAD2
;
292 case XK_KP_3
: return KeyNUMPAD3
;
293 case XK_KP_4
: return KeyNUMPAD4
;
294 case XK_KP_5
: return KeyNUMPAD5
;
295 case XK_KP_6
: return KeyNUMPAD6
;
296 case XK_KP_7
: return KeyNUMPAD7
;
297 case XK_KP_8
: return KeyNUMPAD8
;
298 case XK_KP_9
: return KeyNUMPAD9
;
299 case XK_KP_Enter
: return KeyRETURN
;
300 case XK_KP_Home
: return KeyHOME
;
301 case XK_KP_Left
: return KeyLEFT
;
302 case XK_KP_Up
: return KeyUP
;
303 case XK_KP_Right
: return KeyRIGHT
;
304 case XK_KP_Down
: return KeyDOWN
;
305 case XK_KP_Page_Up
: return KeyPRIOR
;
306 case XK_KP_Page_Down
: return KeyNEXT
;
307 case XK_KP_End
: return KeyEND
;
308 case XK_KP_Begin
: return KeyCANCEL
;
309 case XK_KP_Insert
: return KeyINSERT
;
310 case XK_KP_Delete
: return KeyDELETE
;
311 case XK_KP_Multiply
: return KeyMULTIPLY
;
312 case XK_KP_Add
: return KeyADD
;
313 case XK_KP_Subtract
: return KeySUBTRACT
;
314 case XK_KP_Decimal
: return KeyDECIMAL
;
315 // case XK_period: return KeyDECIMAL;
316 case XK_KP_Divide
: return KeyDIVIDE
;
317 case XK_F1
: return KeyF1
;
318 case XK_F2
: return KeyF2
;
319 case XK_F3
: return KeyF3
;
320 case XK_F4
: return KeyF4
;
321 case XK_F5
: return KeyF5
;
322 case XK_F6
: return KeyF6
;
323 case XK_F7
: return KeyF7
;
324 case XK_F8
: return KeyF8
;
325 case XK_F9
: return KeyF9
;
326 case XK_F10
: return KeyF10
;
327 case XK_F11
: return KeyF11
;
328 case XK_F12
: return KeyF12
;
329 case XK_Shift_L
: return KeySHIFT
;
330 case XK_Shift_R
: return KeySHIFT
;
331 case XK_Control_L
: return KeyCONTROL
;
332 case XK_Control_R
: return KeyCONTROL
;
333 case XK_Caps_Lock
: return KeyCAPITAL
;
334 case XK_Super_L
: return KeyLWIN
;
335 case XK_Super_R
: return KeyRWIN
;
336 case XK_Mode_switch
: return KeyMENU
;
337 case XK_ISO_Level3_Shift
: return KeyMENU
;
338 case XK_Menu
: return KeyAPPS
;
339 case XK_Alt_L
: return KeyMENU
;
340 case XK_Alt_R
: return KeyMENU
;
341 case XK_space
: return KeySPACE
;
343 case XK_a
: return KeyA
;
345 case XK_b
: return KeyB
;
347 case XK_c
: return KeyC
;
349 case XK_d
: return KeyD
;
351 case XK_e
: return KeyE
;
353 case XK_f
: return KeyF
;
355 case XK_g
: return KeyG
;
357 case XK_h
: return KeyH
;
359 case XK_i
: return KeyI
;
361 case XK_j
: return KeyJ
;
363 case XK_k
: return KeyK
;
365 case XK_l
: return KeyL
;
367 case XK_m
: return KeyM
;
369 case XK_n
: return KeyN
;
371 case XK_o
: return KeyO
;
373 case XK_p
: return KeyP
;
375 case XK_q
: return KeyQ
;
377 case XK_r
: return KeyR
;
379 case XK_s
: return KeyS
;
381 case XK_t
: return KeyT
;
383 case XK_u
: return KeyU
;
385 case XK_v
: return KeyV
;
387 case XK_w
: return KeyW
;
389 case XK_x
: return KeyX
;
391 case XK_y
: return KeyY
;
393 case XK_z
: return KeyZ
;
395 // other keys don't need to be processed here
401 // check if the next pressed key is the same
402 static bool keyRepeat(Display
*display
, XEvent
*event
)
406 if (XPending(display
))
408 XPeekEvent(display
, &peekevent
);
410 if ((peekevent
.type
== KeyPress
) &&
411 (peekevent
.xkey
.keycode
== event
->xkey
.keycode
) &&
412 ((peekevent
.xkey
.time
-event
->xkey
.time
) < 2))
419 bool CUnixEventEmitter::processMessage (XEvent
&event
, CEventServer
*server
)
422 server
=&_InternalServer
;
424 XWindowAttributes xwa
;
425 XGetWindowAttributes (_dpy
, _win
, &xwa
);
431 //nlinfo("%d %d %d", event.xbutton.button, event.xbutton.x, event.xbutton.y);
432 float fX
= (float) event
.xbutton
.x
/ (float) xwa
.width
;
433 float fY
= 1.0f
- (float) event
.xbutton
.y
/ (float) xwa
.height
;
434 TMouseButton button
=getMouseButton(event
.xbutton
.state
);
435 switch(event
.xbutton
.button
)
438 server
->postEvent(new CEventMouseDown(fX
, fY
, (TMouseButton
)(leftButton
|(button
&~(leftButton
|middleButton
|rightButton
))), this));
441 server
->postEvent(new CEventMouseDown(fX
, fY
, (TMouseButton
)(middleButton
|(button
&~(leftButton
|middleButton
|rightButton
))), this));
444 server
->postEvent(new CEventMouseDown(fX
, fY
, (TMouseButton
)(rightButton
|(button
&~(leftButton
|middleButton
|rightButton
))), this));
447 server
->postEvent(new CEventMouseWheel(fX
, fY
, button
, true, this));
450 server
->postEvent(new CEventMouseWheel(fX
, fY
, button
, false, this));
457 //nlinfo("%d %d %d", event.xbutton.button, event.xbutton.x, event.xbutton.y);
458 float fX
= (float) event
.xbutton
.x
/ (float) xwa
.width
;
459 float fY
= 1.0f
- (float) event
.xbutton
.y
/ (float) xwa
.height
;
460 switch(event
.xbutton
.button
)
463 server
->postEvent(new CEventMouseUp(fX
, fY
, leftButton
, this));
466 server
->postEvent(new CEventMouseUp(fX
, fY
, middleButton
, this));
469 server
->postEvent(new CEventMouseUp(fX
, fY
, rightButton
, this));
476 TMouseButton button
=getMouseButton (event
.xbutton
.state
);
478 // get the relative mouse position
479 float fX
= (float) event
.xbutton
.x
/ (float) xwa
.width
;
480 float fY
= 1.0f
- (float) event
.xbutton
.y
/ (float) xwa
.height
;
482 // post a normal mouse move event to the event server
483 server
->postEvent (new CEventMouseMove (fX
, fY
, button
, this));
489 // save keycode because XFilterEvent could set it to 0
490 uint keyCode
= event
.xkey
.keycode
;
492 static char Text
[256];
495 // check if event is filtered
496 bool filtered
= XFilterEvent(&event
, _win
);
498 // if key event is filtered, we shouldn't use XLookupString to retrieve KeySym
501 Status status
= XLookupNone
;
503 #ifdef X_HAVE_UTF8_STRING
505 c
= Xutf8LookupString(_ic
, &event
.xkey
, Text
, sizeof(Text
), &k
, &status
);
508 if (status
== XLookupNone
)
509 c
= XLookupString(&event
.xkey
, Text
, sizeof(Text
), &k
, NULL
);
513 k
= XkbKeycodeToKeysym(_dpy
, keyCode
, 0, 0);
516 // send CEventKeyDown event only if keyCode is defined
519 TKey key
= getKeyFromKeySym(k
);
521 key
= getKeyFromKeycode(keyCode
);
523 // search for key in map
524 std::map
<TKey
, bool>::const_iterator it
= _PressedKeys
.find(key
);
526 // if key is not found or value is false, that's the first time
527 bool firstTime
= (it
== _PressedKeys
.end()) || !it
->second
;
529 server
->postEvent (new CEventKeyDown (key
, getKeyButton(event
.xbutton
.state
), firstTime
, this));
530 _PressedKeys
[key
] = true;
532 // don't send a control character when deleting
533 if (key
== KeyDELETE
) c
= 0;
540 #ifdef X_HAVE_UTF8_STRING
541 ::u32string ucstr
= NLMISC::CUtfStringView(Text
).toUtf32();
543 CEventChar
*charEvent
= new CEventChar (ucstr
[0], getKeyButton(event
.xbutton
.state
), this);
545 // raw if not processed by IME
546 charEvent
->setRaw(keyCode
!= 0);
548 server
->postEvent (charEvent
);
550 // FIXME: Convert locale to UTF-32
551 for (int i
= 0; i
< c
; i
++)
553 CEventChar
*charEvent
= new CEventChar ((u32char
)(unsigned char)Text
[i
], getKeyButton(event
.xbutton
.state
), this);
555 // raw if not processed by IME
556 charEvent
->setRaw(keyCode
!= 0);
558 server
->postEvent (charEvent
);
566 if (!keyRepeat(_dpy
, &event
))
569 // only need to get correct KeySym
570 int c
= XLookupString(&event
.xkey
, NULL
, 0, &k
, NULL
);
572 TKey key
= getKeyFromKeySym(k
);
574 key
= getKeyFromKeycode(event
.xkey
.keycode
);
576 server
->postEvent (new CEventKeyUp (key
, getKeyButton(event
.xbutton
.state
), this));
577 _PressedKeys
[key
] = false;
581 case SelectionRequest
:
584 XSelectionRequestEvent req
= event
.xselectionrequest
;
586 respond
.xselection
.type
= SelectionNotify
;
587 respond
.xselection
.display
= req
.display
;
588 respond
.xselection
.requestor
= req
.requestor
;
589 respond
.xselection
.selection
=req
.selection
;
590 respond
.xselection
.target
= req
.target
;
591 respond
.xselection
.time
= req
.time
;
592 respond
.xselection
.property
= req
.property
;
594 if (req
.property
== None
)
596 respond
.xselection
.property
= req
.target
;
598 if (req
.target
== XA_TARGETS
)
607 respond
.xselection
.property
= req
.property
;
609 XChangeProperty(req
.display
, req
.requestor
, req
.property
, XA_ATOM
, 32, PropModeReplace
, (unsigned char *)targets
, 3 /* number of element */);
611 else if (req
.target
== XA_STRING
)
613 respond
.xselection
.property
= req
.property
;
614 std::string str
= _CopiedString
; // NLMISC::CUtfStringView(_CopiedString).toAscii(); // FIXME: Convert UTF-8 to local
615 XChangeProperty(req
.display
, req
.requestor
, req
.property
, XA_STRING
, 8, PropModeReplace
, (const unsigned char*)str
.c_str(), str
.length());
617 else if (req
.target
== XA_UTF8_STRING
)
619 respond
.xselection
.property
= req
.property
;
620 XChangeProperty(req
.display
, req
.requestor
, respond
.xselection
.property
, XA_UTF8_STRING
, 8, PropModeReplace
, (const unsigned char*)_CopiedString
.c_str(), _CopiedString
.length());
624 // Note: Calling XGetAtomName with arbitrary value crash the client, maybe req.target have been sanitized by X11 server
625 respond
.xselection
.property
= None
;
628 XSendEvent (_dpy
, req
.requestor
, 0, 0, &respond
);
633 _SelectionOwned
= false;
634 _CopiedString
.clear();
636 case SelectionNotify
:
638 Atom target
= event
.xselection
.target
;
641 int actualFormat
= 0;
642 unsigned long nitems
= 0, bytesLeft
= 0;
644 // some applications are sending ATOM and other TARGETS
645 if (target
== XA_TARGETS
|| target
== XA_ATOM
)
647 Atom
*supportedTargets
= NULL
;
649 // list NeL selection properties
650 if (XGetWindowProperty(_dpy
, _win
, XA_NEL_SEL
, 0, XMaxRequestSize(_dpy
), False
, AnyPropertyType
, &actualType
, &actualFormat
, &nitems
, &bytesLeft
, (unsigned char**)&supportedTargets
) != Success
)
655 nlwarning("Paste: Supported TARGETS list too long.");
659 sint bestTargetElect
= 0;
662 for (uint i
=0; i
< nitems
; i
++)
664 // nlwarning(" - Type=%s (%u)", XGetAtomName(_dpy, supportedTargets[i]), (uint)supportedTargets[i]);
665 if (supportedTargets
[i
] == XA_UTF8_STRING
)
667 if (bestTargetElect
< 2)
669 bestTarget
= XA_UTF8_STRING
;
673 else if (supportedTargets
[i
] == XA_STRING
)
675 if (bestTargetElect
< 1)
677 bestTarget
= XA_STRING
;
683 XFree(supportedTargets
);
685 if (!bestTargetElect
)
687 nlwarning("Paste buffer is not a text buffer.");
691 // request string conversion
692 XConvertSelection(_dpy
, XA_CLIPBOARD
, bestTarget
, XA_NEL_SEL
, _win
, CurrentTime
);
694 else if (target
== XA_UTF8_STRING
|| target
== XA_STRING
)
699 if (XGetWindowProperty(_dpy
, _win
, XA_NEL_SEL
, 0, XMaxRequestSize(_dpy
), False
, AnyPropertyType
, &actualType
, &actualFormat
, &nitems
, &bytesLeft
, (unsigned char**)&data
) != Success
)
702 std::string text
= (const char*)data
;
705 // convert buffer to ucstring
706 if (target
== XA_UTF8_STRING
)
710 else if (target
== XA_STRING
)
712 // FIXME: Convert local to UTF-8
713 // text = NLMISC::CUtfStringView(text).toAscii();
717 nlwarning("Unknown format %u", (uint
)target
);
720 // sent string event to event server
721 server
->postEvent (new CEventString (text
, this));
725 nlwarning("Unknow target %u", (uint
)target
);
732 #ifdef X_HAVE_UTF8_STRING
733 if (_ic
) XSetICFocus(_ic
);
735 server
->postEvent (new CEventSetFocus (true, this));
736 // server->postEvent(new CEventActivate(true, this));
740 #ifdef X_HAVE_UTF8_STRING
741 if (_ic
) XUnsetICFocus(_ic
);
743 server
->postEvent (new CEventSetFocus (false, this));
744 // server->postEvent(new CEventActivate(false, this));
750 XRefreshKeyboardMapping((XMappingEvent
*)&event
);
753 // XIM server has crashed
757 if ((event
.xclient
.format
== 32) && ((Atom
)event
.xclient
.data
.l
[0] == XA_WM_DELETE_WINDOW
))
759 server
->postEvent(new CEventDestroyWindow(this));
763 // nlinfo("UnknownEvent");
764 // XtDispatchEvent(&event);
771 bool CUnixEventEmitter::copyTextToClipboard(const std::string
&text
)
773 _CopiedString
= text
;
775 // NeL window is the owner of clipboard
776 XSetSelectionOwner(_dpy
, XA_CLIPBOARD
, _win
, CurrentTime
);
778 // check we are owning the clipboard
779 if (XGetSelectionOwner(_dpy
, XA_CLIPBOARD
) != _win
)
781 nlwarning("Can't aquire selection");
785 _SelectionOwned
= true;
790 bool CUnixEventEmitter::pasteTextFromClipboard(std::string
&text
)
792 // check if we own the selection
795 text
= _CopiedString
;
799 // check if there is a data in clipboard
800 if (XGetSelectionOwner(_dpy
, XA_CLIPBOARD
) == None
)
803 // request supported methods
804 XConvertSelection(_dpy
, XA_CLIPBOARD
, XA_TARGETS
, XA_NEL_SEL
, _win
, CurrentTime
);
806 // don't return result now
812 #endif // defined(NL_OS_UNIX) && !defined(NL_OS_MAC)