Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / 3d / driver / opengl / unix_event_emitter.cpp
blobe8e8ae99fcc048be0c401b26016f90743b924ab3
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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>
7 //
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)
26 #include <X11/Xlib.h>
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"
34 #ifdef DEBUG_NEW
35 #define new DEBUG_NEW
36 #endif
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;
46 namespace NLMISC {
48 CUnixEventEmitter::CUnixEventEmitter ():_dpy(NULL), _win(0), _im(NULL), _ic(NULL), _driver(NULL)
50 _SelectionOwned = false;
53 CUnixEventEmitter::~CUnixEventEmitter()
55 closeIM();
58 void CUnixEventEmitter::init(Display *dpy, Window win, NL3D::IDriver *driver)
60 _dpy = dpy;
61 _win = win;
62 _driver = 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
84 createIM();
87 void CUnixEventEmitter::createIM()
89 #ifdef X_HAVE_UTF8_STRING
91 _im = XOpenIM(_dpy, NULL, NULL, NULL);
93 if (_im == NULL)
95 XSetLocaleModifiers("@im=local");
97 _im = XOpenIM(_dpy, NULL, NULL, NULL);
99 if (_im == NULL)
101 XSetLocaleModifiers("@im=");
103 _im = XOpenIM(_dpy, NULL, NULL, NULL);
105 if (_im == NULL)
107 nlwarning("XOpenIM failed");
112 if (_im)
114 _ic = XCreateIC(_im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, _win, XNFocusWindow, _win, NULL);
116 if (!_ic)
118 nlwarning("XCreateIC failed");
121 #endif
124 void CUnixEventEmitter::closeIM()
126 #ifdef X_HAVE_UTF8_STRING
127 if (_ic)
129 XDestroyIC(_ic);
130 _ic = 0;
133 if (_im)
135 XCloseIM(_im);
136 _im = 0;
138 #endif
141 void CUnixEventEmitter::submitEvents(CEventServer & server, bool allWindows)
143 while (XPending(_dpy))
145 XEvent Event;
146 XNextEvent(_dpy, &Event);
147 if (allWindows || Event.xany.window == _win)
149 // nlinfo("event: %d", Event.type);
150 if (_driver)
152 // forward X events to OpenGL driver
153 x11Proc proc = (x11Proc)_driver->getWindowProc();
155 if (proc)
156 proc(_driver, &Event);
158 else
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);
175 #ifndef AltMask
176 # define AltMask (Mod1Mask)
177 #endif
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
205 switch (keycode)
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;
257 default:
258 // nlwarning("missing keycode 0x%x %d '%c'", keycode, keycode, keycode);
259 break;
262 return KeyNOKEY;
265 TKey getKeyFromKeySym (KeySym keysym)
267 // nlwarning("0x%x %d '%c'", keysym, keysym, keysym);
268 switch (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;
342 case XK_A:
343 case XK_a: return KeyA;
344 case XK_B:
345 case XK_b: return KeyB;
346 case XK_C:
347 case XK_c: return KeyC;
348 case XK_D:
349 case XK_d: return KeyD;
350 case XK_E:
351 case XK_e: return KeyE;
352 case XK_F:
353 case XK_f: return KeyF;
354 case XK_G:
355 case XK_g: return KeyG;
356 case XK_H:
357 case XK_h: return KeyH;
358 case XK_I:
359 case XK_i: return KeyI;
360 case XK_J:
361 case XK_j: return KeyJ;
362 case XK_K:
363 case XK_k: return KeyK;
364 case XK_L:
365 case XK_l: return KeyL;
366 case XK_M:
367 case XK_m: return KeyM;
368 case XK_N:
369 case XK_n: return KeyN;
370 case XK_O:
371 case XK_o: return KeyO;
372 case XK_P:
373 case XK_p: return KeyP;
374 case XK_Q:
375 case XK_q: return KeyQ;
376 case XK_R:
377 case XK_r: return KeyR;
378 case XK_S:
379 case XK_s: return KeyS;
380 case XK_T:
381 case XK_t: return KeyT;
382 case XK_U:
383 case XK_u: return KeyU;
384 case XK_V:
385 case XK_v: return KeyV;
386 case XK_W:
387 case XK_w: return KeyW;
388 case XK_X:
389 case XK_x: return KeyX;
390 case XK_Y:
391 case XK_y: return KeyY;
392 case XK_Z:
393 case XK_z: return KeyZ;
394 default:
395 // other keys don't need to be processed here
396 break;
398 return KeyNOKEY;
401 // check if the next pressed key is the same
402 static bool keyRepeat(Display *display, XEvent *event)
404 XEvent peekevent;
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))
413 return true;
416 return false;
419 bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server)
421 if (!server)
422 server=&_InternalServer;
424 XWindowAttributes xwa;
425 XGetWindowAttributes (_dpy, _win, &xwa);
427 switch (event.type)
429 case ButtonPress:
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)
437 case Button1:
438 server->postEvent(new CEventMouseDown(fX, fY, (TMouseButton)(leftButton|(button&~(leftButton|middleButton|rightButton))), this));
439 break;
440 case Button2:
441 server->postEvent(new CEventMouseDown(fX, fY, (TMouseButton)(middleButton|(button&~(leftButton|middleButton|rightButton))), this));
442 break;
443 case Button3:
444 server->postEvent(new CEventMouseDown(fX, fY, (TMouseButton)(rightButton|(button&~(leftButton|middleButton|rightButton))), this));
445 break;
446 case Button4:
447 server->postEvent(new CEventMouseWheel(fX, fY, button, true, this));
448 break;
449 case Button5:
450 server->postEvent(new CEventMouseWheel(fX, fY, button, false, this));
451 break;
453 break;
455 case ButtonRelease:
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)
462 case Button1:
463 server->postEvent(new CEventMouseUp(fX, fY, leftButton, this));
464 break;
465 case Button2:
466 server->postEvent(new CEventMouseUp(fX, fY, middleButton, this));
467 break;
468 case Button3:
469 server->postEvent(new CEventMouseUp(fX, fY, rightButton, this));
470 break;
472 break;
474 case MotionNotify:
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));
485 break;
487 case KeyPress:
489 // save keycode because XFilterEvent could set it to 0
490 uint keyCode = event.xkey.keycode;
491 KeySym k;
492 static char Text[256];
493 int c = 0;
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
499 if (!filtered)
501 Status status = XLookupNone;
503 #ifdef X_HAVE_UTF8_STRING
504 if (_ic)
505 c = Xutf8LookupString(_ic, &event.xkey, Text, sizeof(Text), &k, &status);
506 #endif
508 if (status == XLookupNone)
509 c = XLookupString(&event.xkey, Text, sizeof(Text), &k, NULL);
511 else
513 k = XkbKeycodeToKeysym(_dpy, keyCode, 0, 0);
516 // send CEventKeyDown event only if keyCode is defined
517 if (keyCode)
519 TKey key = getKeyFromKeySym(k);
520 if (key == KeyNOKEY)
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;
536 Text[c] = '\0';
538 if (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);
549 #else
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);
560 #endif
562 break;
564 case KeyRelease:
566 if (!keyRepeat(_dpy, &event))
568 KeySym k;
569 // only need to get correct KeySym
570 int c = XLookupString(&event.xkey, NULL, 0, &k, NULL);
572 TKey key = getKeyFromKeySym(k);
573 if(key == KeyNOKEY)
574 key = getKeyFromKeycode(event.xkey.keycode);
576 server->postEvent (new CEventKeyUp (key, getKeyButton(event.xbutton.state), this));
577 _PressedKeys[key] = false;
579 break;
581 case SelectionRequest:
583 XEvent respond;
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)
600 Atom targets[] =
602 XA_TARGETS,
603 XA_STRING,
604 XA_UTF8_STRING
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());
622 else
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);
630 break;
632 case SelectionClear:
633 _SelectionOwned = false;
634 _CopiedString.clear();
635 break;
636 case SelectionNotify:
638 Atom target = event.xselection.target;
640 Atom actualType = 0;
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)
651 return false;
653 if (bytesLeft > 0)
655 nlwarning("Paste: Supported TARGETS list too long.");
658 Atom bestTarget = 0;
659 sint bestTargetElect = 0;
661 // Elect best type
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;
670 bestTargetElect = 2;
673 else if (supportedTargets[i] == XA_STRING )
675 if (bestTargetElect < 1)
677 bestTarget = XA_STRING;
678 bestTargetElect = 1;
683 XFree(supportedTargets);
685 if (!bestTargetElect)
687 nlwarning("Paste buffer is not a text buffer.");
688 return false;
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)
696 uint8 *data = NULL;
698 // get selection
699 if (XGetWindowProperty(_dpy, _win, XA_NEL_SEL, 0, XMaxRequestSize(_dpy), False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesLeft, (unsigned char**)&data) != Success)
700 return false;
702 std::string text = (const char*)data;
703 XFree(data);
705 // convert buffer to ucstring
706 if (target == XA_UTF8_STRING)
708 // OK
710 else if (target == XA_STRING)
712 // FIXME: Convert local to UTF-8
713 // text = NLMISC::CUtfStringView(text).toAscii();
715 else
717 nlwarning("Unknown format %u", (uint)target);
720 // sent string event to event server
721 server->postEvent (new CEventString (text, this));
723 else
725 nlwarning("Unknow target %u", (uint)target);
728 break;
730 case FocusIn:
731 // keyboard focus
732 #ifdef X_HAVE_UTF8_STRING
733 if (_ic) XSetICFocus(_ic);
734 #endif
735 server->postEvent (new CEventSetFocus (true, this));
736 // server->postEvent(new CEventActivate(true, this));
737 break;
738 case FocusOut:
739 // keyboard focus
740 #ifdef X_HAVE_UTF8_STRING
741 if (_ic) XUnsetICFocus(_ic);
742 #endif
743 server->postEvent (new CEventSetFocus (false, this));
744 // server->postEvent(new CEventActivate(false, this));
745 break;
746 case KeymapNotify:
747 break;
748 case MappingNotify:
749 // update keymap
750 XRefreshKeyboardMapping((XMappingEvent *)&event);
751 break;
752 case DestroyNotify:
753 // XIM server has crashed
754 createIM();
755 break;
756 case ClientMessage:
757 if ((event.xclient.format == 32) && ((Atom)event.xclient.data.l[0] == XA_WM_DELETE_WINDOW))
759 server->postEvent(new CEventDestroyWindow(this));
761 break;
762 default:
763 // nlinfo("UnknownEvent");
764 // XtDispatchEvent(&event);
765 return false;
768 return true;
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");
782 return false;
785 _SelectionOwned = true;
787 return true;
790 bool CUnixEventEmitter::pasteTextFromClipboard(std::string &text)
792 // check if we own the selection
793 if (_SelectionOwned)
795 text = _CopiedString;
796 return true;
799 // check if there is a data in clipboard
800 if (XGetSelectionOwner(_dpy, XA_CLIPBOARD) == None)
801 return false;
803 // request supported methods
804 XConvertSelection(_dpy, XA_CLIPBOARD, XA_TARGETS, XA_NEL_SEL, _win, CurrentTime);
806 // don't return result now
807 return false;
810 } // NLMISC
812 #endif // defined(NL_OS_UNIX) && !defined(NL_OS_MAC)