3 * ***** BEGIN GPL LICENSE BLOCK *****
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20 * All rights reserved.
22 * The Original Code is: all of this file.
24 * Contributor(s): none yet.
26 * Part of this code has been taken from Qt, under LGPL license
27 * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
29 * ***** END GPL LICENSE BLOCK *****
36 #include "GHOST_SystemX11.h"
37 #include "GHOST_WindowX11.h"
38 #include "GHOST_WindowManager.h"
39 #include "GHOST_TimerManager.h"
40 #include "GHOST_EventCursor.h"
41 #include "GHOST_EventKey.h"
42 #include "GHOST_EventButton.h"
43 #include "GHOST_EventWheel.h"
44 #include "GHOST_EventNDOF.h"
45 #include "GHOST_NDOFManager.h"
46 #include "GHOST_DisplayManagerX11.h"
48 #include "GHOST_Debug.h"
50 #include <X11/Xatom.h>
51 #include <X11/keysym.h>
52 #include <X11/XKBlib.h> /* allow detectable autorepeate */
56 #if defined(_SGI_EXTRA_PREDEFINES) && !defined(NO_FAST_ATOMS)
57 #include <X11/SGIFastAtom.h>
59 #define XSGIFastInternAtom(dpy,string,fast_name,how) XInternAtom(dpy,string,how)
71 #include <stdio.h> // for fprintf only
72 #include <cstdlib> // for exit
74 typedef struct NDOFPlatformInfo {
77 volatile GHOST_TEventNDOFData *currValues;
84 static NDOFPlatformInfo sNdofInfo = {NULL, 0, NULL, 0, 0, 0, 0};
87 //these are for copy and select copy
88 static char *txt_cut_buffer= NULL;
89 static char *txt_select_buffer= NULL;
99 m_display = XOpenDisplay(NULL);
102 std::cerr << "Unable to open a display" << std::endl;
103 abort(); //was return before, but this would just mean it will crash later
108 = XSGIFastInternAtom(m_display,
110 SGI_XA_WM_DELETE_WINDOW, False);
113 = XInternAtom(m_display, "WM_DELETE_WINDOW", True);
116 m_wm_protocols= XInternAtom(m_display, "WM_PROTOCOLS", False);
117 m_wm_take_focus= XInternAtom(m_display, "WM_TAKE_FOCUS", False);
118 m_wm_state= XInternAtom(m_display, "WM_STATE", False);
119 m_wm_change_state= XInternAtom(m_display, "WM_CHANGE_STATE", False);
120 m_net_state= XInternAtom(m_display, "_NET_WM_STATE", False);
121 m_net_max_horz= XInternAtom(m_display,
122 "_NET_WM_STATE_MAXIMIZED_HORZ", False);
123 m_net_max_vert= XInternAtom(m_display,
124 "_NET_WM_STATE_MAXIMIZED_VERT", False);
125 m_net_fullscreen= XInternAtom(m_display,
126 "_NET_WM_STATE_FULLSCREEN", False);
127 m_motif= XInternAtom(m_display, "_MOTIF_WM_HINTS", False);
128 m_targets= XInternAtom(m_display, "TARGETS", False);
129 m_string= XInternAtom(m_display, "STRING", False);
130 m_compound_text= XInternAtom(m_display, "COMPOUND_TEXT", False);
131 m_text= XInternAtom(m_display, "TEXT", False);
132 m_clipboard= XInternAtom(m_display, "CLIPBOARD", False);
133 m_primary= XInternAtom(m_display, "PRIMARY", False);
134 m_xclip_out= XInternAtom(m_display, "XCLIP_OUT", False);
135 m_incr= XInternAtom(m_display, "INCR", False);
136 m_utf8_string= XInternAtom(m_display, "UTF8_STRING", False);
140 // compute the initial time
142 if (gettimeofday(&tv,NULL) == -1) {
143 GHOST_ASSERT(false,"Could not instantiate timer!");
146 m_start_time = GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000);
149 /* use detectable autorepeate, mac and windows also do this */
151 int xkb_opcode, xkb_event, xkb_error;
152 int xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion;
154 use_xkb = XkbQueryExtension(m_display, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
156 XkbSetDetectableAutoRepeat(m_display, true, NULL);
164 XCloseDisplay(m_display);
172 GHOST_TSuccess success = GHOST_System::init();
175 m_displayManager = new GHOST_DisplayManagerX11(this);
177 if (m_displayManager) {
178 return GHOST_kSuccess;
182 return GHOST_kFailure;
190 if (gettimeofday(&tv,NULL) == -1) {
191 GHOST_ASSERT(false,"Could not compute time!");
194 return GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000) - m_start_time;
201 return GHOST_TUns8(1);
205 * Returns the dimensions of the main display on this system.
206 * @return The dimension of the main display.
210 getMainDisplayDimensions(
215 width = DisplayWidth(m_display, DefaultScreen(m_display));
216 height = DisplayHeight(m_display, DefaultScreen(m_display));
221 * Create a new window.
222 * The new window is added to the list of windows managed.
223 * Never explicitly delete the window, use disposeWindow() instead.
224 * @param title The name of the window (displayed in the title bar of the window if the OS supports it).
225 * @param left The coordinate of the left edge of the window.
226 * @param top The coordinate of the top edge of the window.
227 * @param width The width the window.
228 * @param height The height the window.
229 * @param state The state of the window when opened.
230 * @param type The type of drawing context installed in this window.
231 * @param stereoVisual Stereo visual for quad buffered stereo.
232 * @param numOfAASamples Number of samples used for AA (zero if no AA)
233 * @param parentWindow Parent (embedder) window
234 * @return The new window (or 0 if creation failed).
239 const STR_String& title,
244 GHOST_TWindowState state,
245 GHOST_TDrawingContextType type,
247 const GHOST_TUns16 numOfAASamples,
248 const GHOST_TEmbedderWindowID parentWindow
250 GHOST_WindowX11 * window = 0;
252 if (!m_display) return 0;
257 window = new GHOST_WindowX11 (
258 this,m_display,title, left, top, width, height, state, parentWindow, type, stereoVisual
262 // Both are now handle in GHOST_WindowX11.cpp
263 // Focus and Delete atoms.
265 if (window->getValid()) {
266 // Store the pointer to the window
267 m_windowManager->addWindow(window);
269 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
285 if (xwind == 0) return NULL;
287 // It is not entirely safe to do this as the backptr may point
288 // to a window that has recently been removed.
289 // We should always check the window manager's list of windows
290 // and only process events on these windows.
292 vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
294 vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
295 vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
297 for (; win_it != win_end; ++win_it) {
298 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
299 if (window->getXWindow() == xwind) {
307 static void SleepTillEvent(Display *display, GHOST_TInt64 maxSleep) {
308 int fd = ConnectionNumber(display);
314 if (maxSleep == -1) {
315 select(fd + 1, &fds, NULL, NULL, NULL);
319 tv.tv_sec = maxSleep/1000;
320 tv.tv_usec = (maxSleep - tv.tv_sec*1000)*1000;
322 select(fd + 1, &fds, NULL, NULL, &tv);
326 /* This function borrowed from Qt's X11 support
329 struct init_timestamp_data
334 static Bool init_timestamp_scanner(Display*, XEvent *event, XPointer arg)
336 init_timestamp_data *data =
337 reinterpret_cast<init_timestamp_data*>(arg);
342 data->timestamp = event->xbutton.time;
345 data->timestamp = event->xmotion.time;
349 data->timestamp = event->xkey.time;
352 data->timestamp = event->xproperty.time;
356 data->timestamp = event->xcrossing.time;
359 data->timestamp = event->xselectionclear.time;
370 lastEventTime(Time default_time) {
371 init_timestamp_data data;
372 data.timestamp = default_time;
374 XCheckIfEvent(m_display, &ev, &init_timestamp_scanner, (XPointer)&data);
376 return data.timestamp;
386 // Get all the current events -- translate them into
387 // ghost events and call base class pushEvent() method.
389 bool anyProcessed = false;
392 GHOST_TimerManager* timerMgr = getTimerManager();
394 if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
395 GHOST_TUns64 next = timerMgr->nextFireTime();
397 if (next==GHOST_kFireTimeNever) {
398 SleepTillEvent(m_display, -1);
400 GHOST_TInt64 maxSleep = next - getMilliSeconds();
403 SleepTillEvent(m_display, next - getMilliSeconds());
407 if (timerMgr->fireTimers(getMilliSeconds())) {
411 while (XPending(m_display)) {
413 XNextEvent(m_display, &xevent);
414 processEvent(&xevent);
418 if (generateWindowExposeEvents()) {
421 } while (waitForEvent && !anyProcessed);
427 GHOST_SystemX11::processEvent(XEvent *xe)
429 GHOST_WindowX11 * window = findGhostWindow(xe->xany.window);
430 GHOST_Event * g_event = NULL;
439 XExposeEvent & xee = xe->xexpose;
441 if (xee.count == 0) {
442 // Only generate a single expose event
443 // per read of the event queue.
448 GHOST_kEventWindowUpdate,
457 XMotionEvent &xme = xe->xmotion;
459 if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
461 GHOST_TInt32 x_new= xme.x_root;
462 GHOST_TInt32 y_new= xme.y_root;
463 GHOST_TInt32 x_accum, y_accum;
466 /* fallback to window bounds */
467 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
468 window->getClientBounds(bounds);
470 /* could also clamp to screen bounds
471 * wrap with a window outside the view will fail atm */
472 bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */
473 window->getCursorGrabAccum(x_accum, y_accum);
475 if(x_new != xme.x_root || y_new != xme.y_root) {
476 if (xme.time > m_last_warp) {
477 /* when wrapping we don't need to add an event because the
478 * setCursorPosition call will cause a new event after */
479 setCursorPosition(x_new, y_new); /* wrap */
480 window->setCursorGrabAccum(x_accum + (xme.x_root - x_new), y_accum + (xme.y_root - y_new));
481 m_last_warp = lastEventTime(xme.time);
483 setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
490 GHOST_kEventCursorMove,
492 xme.x_root + x_accum,
501 GHOST_kEventCursorMove,
513 XKeyEvent *xke = &(xe->xkey);
515 KeySym key_sym = XLookupKeysym(xke,0);
518 GHOST_TKey gkey = convertXKey(key_sym);
519 GHOST_TEventType type = (xke->type == KeyPress) ?
520 GHOST_kEventKeyDown : GHOST_kEventKeyUp;
522 if (!XLookupString(xke, &ascii, 1, NULL, NULL)) {
540 /* process wheel mouse events and break */
541 if (xe->xbutton.button == 4) {
542 g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1);
545 if (xe->xbutton.button == 5) {
546 g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1);
553 XButtonEvent & xbe = xe->xbutton;
554 GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
555 switch (xbe.button) {
556 case Button1 : gbmask = GHOST_kButtonMaskLeft; break;
557 case Button3 : gbmask = GHOST_kButtonMaskRight; break;
558 /* It seems events 6 and 7 are for horizontal scrolling.
559 * you can re-order button mapping like this... (swaps 6,7 with 8,9)
560 * xmodmap -e "pointer = 1 2 3 4 5 8 9 6 7"
562 case 8 : gbmask = GHOST_kButtonMaskButton4; break; /* Button4 is the wheel */
563 case 9 : gbmask = GHOST_kButtonMaskButton5; break; /* Button5 is a wheel too */
565 case Button2 : gbmask = GHOST_kButtonMaskMiddle; break;
568 GHOST_TEventType type = (xbe.type == ButtonPress) ?
569 GHOST_kEventButtonDown : GHOST_kEventButtonUp;
581 // change of size, border, layer etc.
582 case ConfigureNotify:
584 /* XConfigureEvent & xce = xe->xconfigure; */
589 GHOST_kEventWindowSize,
598 XFocusChangeEvent &xfe = xe->xfocus;
600 // May have to look at the type of event and filter some
603 GHOST_TEventType gtype = (xfe.type == FocusIn) ?
604 GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate;
617 XClientMessageEvent & xcme = xe->xclient;
620 if (((Atom)xcme.data.l[0]) == m_delete_window_atom) {
624 GHOST_kEventWindowClose,
629 if (sNdofInfo.currValues) {
630 static GHOST_TEventNDOFData data = {0,0,0,0,0,0,0,0,0,0,0};
631 if (xcme.message_type == sNdofInfo.motionAtom)
634 data.delta = xcme.data.s[8] - data.time;
635 data.time = xcme.data.s[8];
636 data.tx = xcme.data.s[2] >> 2;
637 data.ty = xcme.data.s[3] >> 2;
638 data.tz = xcme.data.s[4] >> 2;
639 data.rx = xcme.data.s[5];
640 data.ry = xcme.data.s[6];
641 data.rz =-xcme.data.s[7];
642 g_event = new GHOST_EventNDOF(getMilliSeconds(),
643 GHOST_kEventNDOFMotion,
645 } else if (xcme.message_type == sNdofInfo.btnPressAtom) {
647 data.delta = xcme.data.s[8] - data.time;
648 data.time = xcme.data.s[8];
649 data.buttons = xcme.data.s[2];
650 g_event = new GHOST_EventNDOF(getMilliSeconds(),
651 GHOST_kEventNDOFButton,
654 } else if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
655 XWindowAttributes attr;
659 /* as ICCCM say, we need reply this event
660 * with a SetInputFocus, the data[1] have
661 * the valid timestamp (send by the wm).
663 * Some WM send this event before the
664 * window is really mapped (for example
665 * change from virtual desktop), so we need
666 * to be sure that our windows is mapped
667 * or this call fail and close blender.
669 if (XGetWindowAttributes(m_display, xcme.window, &attr) == True) {
670 if (XGetInputFocus(m_display, &fwin, &revert_to) == True) {
671 if (attr.map_state == IsViewable) {
672 if (fwin != xcme.window)
673 XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
678 /* Unknown client message, ignore */
685 // We're not interested in the following things.(yet...)
687 case GraphicsExpose :
693 // XCrossingEvents pointer leave enter window.
694 // also do cursor move here, MotionNotify only
695 // happens when motion starts & ends inside window
696 XCrossingEvent &xce = xe->xcrossing;
701 GHOST_kEventCursorMove,
711 * [ Clients can select for StructureNotify on their
712 * top-level windows to track transition between
713 * Normal and Iconic states. Receipt of a MapNotify
714 * event will indicate a transition to the Normal
715 * state, and receipt of an UnmapNotify event will
716 * indicate a transition to the Iconic state. ]
718 if (window->m_post_init == True) {
720 * Now we are sure that the window is
721 * mapped, so only need change the state.
723 window->setState (window->m_post_state);
724 window->m_post_init = False;
732 case SelectionRequest:
735 Atom target, string, compound_text, c_string;
736 XSelectionRequestEvent *xse = &xe->xselectionrequest;
738 target = XInternAtom(m_display, "TARGETS", False);
739 string = XInternAtom(m_display, "STRING", False);
740 compound_text = XInternAtom(m_display, "COMPOUND_TEXT", False);
741 c_string = XInternAtom(m_display, "C_STRING", False);
743 /* support obsolete clients */
744 if (xse->property == None) {
745 xse->property = xse->target;
748 nxe.xselection.type = SelectionNotify;
749 nxe.xselection.requestor = xse->requestor;
750 nxe.xselection.property = xse->property;
751 nxe.xselection.display = xse->display;
752 nxe.xselection.selection = xse->selection;
753 nxe.xselection.target = xse->target;
754 nxe.xselection.time = xse->time;
756 /*Check to see if the requestor is asking for String*/
757 if(xse->target == string || xse->target == compound_text || xse->target == c_string) {
758 if (xse->selection == XInternAtom(m_display, "PRIMARY", False)) {
759 XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 8, PropModeReplace, (unsigned char*)txt_select_buffer, strlen(txt_select_buffer));
760 } else if (xse->selection == XInternAtom(m_display, "CLIPBOARD", False)) {
761 XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 8, PropModeReplace, (unsigned char*)txt_cut_buffer, strlen(txt_cut_buffer));
763 } else if (xse->target == target) {
767 alist[2] = compound_text;
769 XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 32, PropModeReplace, (unsigned char*)alist, 4);
772 //Change property to None because we do not support anything but STRING
773 nxe.xselection.property = None;
776 //Send the event to the client 0 0 == False, SelectionNotify
777 XSendEvent(m_display, xse->requestor, 0, 0, &nxe);
783 if(xe->type == window->GetXTablet().MotionEvent)
785 XDeviceMotionEvent* data = (XDeviceMotionEvent*)xe;
786 window->GetXTablet().CommonData.Pressure=
787 data->axis_data[2]/((float)window->GetXTablet().PressureLevels);
789 /* the (short) cast and the &0xffff is bizarre and unexplained anywhere,
790 * but I got garbage data without it. Found it in the xidump.c source --matt */
791 window->GetXTablet().CommonData.Xtilt=
792 (short)(data->axis_data[3]&0xffff)/((float)window->GetXTablet().XtiltLevels);
793 window->GetXTablet().CommonData.Ytilt=
794 (short)(data->axis_data[4]&0xffff)/((float)window->GetXTablet().YtiltLevels);
796 else if(xe->type == window->GetXTablet().ProxInEvent)
798 XProximityNotifyEvent* data = (XProximityNotifyEvent*)xe;
799 if(data->deviceid == window->GetXTablet().StylusID)
800 window->GetXTablet().CommonData.Active= GHOST_kTabletModeStylus;
801 else if(data->deviceid == window->GetXTablet().EraserID)
802 window->GetXTablet().CommonData.Active= GHOST_kTabletModeEraser;
804 else if(xe->type == window->GetXTablet().ProxOutEvent)
805 window->GetXTablet().CommonData.Active= GHOST_kTabletModeNone;
818 prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
820 const vector<GHOST_IWindow*>& v(m_windowManager->getWindows());
822 sNdofInfo.window = static_cast<GHOST_WindowX11*>(v[0])->getXWindow();
823 sNdofInfo.display = m_display;
824 sNdofInfo.currValues = currentNdofValues;
825 return (void*)&sNdofInfo;
831 GHOST_ModifierKeys& keys
834 // analyse the masks retuned from XQueryPointer.
836 memset((void *)m_keyboard_vector,0,sizeof(m_keyboard_vector));
838 XQueryKeymap(m_display,(char *)m_keyboard_vector);
840 // now translate key symobols into keycodes and
843 const KeyCode shift_l = XKeysymToKeycode(m_display,XK_Shift_L);
844 const KeyCode shift_r = XKeysymToKeycode(m_display,XK_Shift_R);
845 const KeyCode control_l = XKeysymToKeycode(m_display,XK_Control_L);
846 const KeyCode control_r = XKeysymToKeycode(m_display,XK_Control_R);
847 const KeyCode alt_l = XKeysymToKeycode(m_display,XK_Alt_L);
848 const KeyCode alt_r = XKeysymToKeycode(m_display,XK_Alt_R);
851 if ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) {
852 keys.set(GHOST_kModifierKeyLeftShift,true);
854 keys.set(GHOST_kModifierKeyLeftShift,false);
856 if ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) {
858 keys.set(GHOST_kModifierKeyRightShift,true);
860 keys.set(GHOST_kModifierKeyRightShift,false);
864 if ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) {
865 keys.set(GHOST_kModifierKeyLeftControl,true);
867 keys.set(GHOST_kModifierKeyLeftControl,false);
869 if ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) {
870 keys.set(GHOST_kModifierKeyRightControl,true);
872 keys.set(GHOST_kModifierKeyRightControl,false);
876 if ((m_keyboard_vector[alt_l >> 3] >> (alt_l & 7)) & 1) {
877 keys.set(GHOST_kModifierKeyLeftAlt,true);
879 keys.set(GHOST_kModifierKeyLeftAlt,false);
881 if ((m_keyboard_vector[alt_r >> 3] >> (alt_r & 7)) & 1) {
882 keys.set(GHOST_kModifierKeyRightAlt,true);
884 keys.set(GHOST_kModifierKeyRightAlt,false);
886 return GHOST_kSuccess;
892 GHOST_Buttons& buttons
895 Window root_return, child_return;
897 unsigned int mask_return;
901 RootWindow(m_display,DefaultScreen(m_display)),
908 return GHOST_kFailure;
911 if (mask_return & Button1Mask) {
912 buttons.set(GHOST_kButtonMaskLeft,true);
914 buttons.set(GHOST_kButtonMaskLeft,false);
917 if (mask_return & Button2Mask) {
918 buttons.set(GHOST_kButtonMaskMiddle,true);
920 buttons.set(GHOST_kButtonMaskMiddle,false);
923 if (mask_return & Button3Mask) {
924 buttons.set(GHOST_kButtonMaskRight,true);
926 buttons.set(GHOST_kButtonMaskRight,false);
930 return GHOST_kSuccess;
941 Window root_return, child_return;
943 unsigned int mask_return;
947 RootWindow(m_display,DefaultScreen(m_display)),
954 return GHOST_kFailure;
959 return GHOST_kSuccess;
970 // This is a brute force move in screen coordinates
971 // XWarpPointer does relative moves so first determine the
972 // current pointer position.
975 if (getCursorPosition(cx,cy) == GHOST_kFailure) {
976 return GHOST_kFailure;
982 XWarpPointer(m_display,None,None,0,0,0,0,relx,rely);
983 XSync(m_display, 0); /* Sync to process all requests */
985 return GHOST_kSuccess;
992 GHOST_WindowX11 * bad_wind
995 GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)");
997 m_dirty_windows.push_back(bad_wind);
1003 generateWindowExposeEvents(
1006 vector<GHOST_WindowX11 *>::iterator w_start = m_dirty_windows.begin();
1007 vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end();
1008 bool anyProcessed = false;
1010 for (;w_start != w_end; ++w_start) {
1011 GHOST_Event * g_event = new
1014 GHOST_kEventWindowUpdate,
1018 (*w_start)->validate();
1022 anyProcessed = true;
1026 m_dirty_windows.clear();
1027 return anyProcessed;
1030 #define GXMAP(k,x,y) case x: k = y; break;
1039 if ((key >= XK_A) && (key <= XK_Z)) {
1040 type = GHOST_TKey( key - XK_A + int(GHOST_kKeyA));
1041 } else if ((key >= XK_a) && (key <= XK_z)) {
1042 type = GHOST_TKey(key - XK_a + int(GHOST_kKeyA));
1043 } else if ((key >= XK_0) && (key <= XK_9)) {
1044 type = GHOST_TKey(key - XK_0 + int(GHOST_kKey0));
1045 } else if ((key >= XK_F1) && (key <= XK_F24)) {
1046 type = GHOST_TKey(key - XK_F1 + int(GHOST_kKeyF1));
1047 #if defined(__sun) || defined(__sun__)
1048 /* This is a bit of a hack, but it looks like sun
1049 Used F11 and friends for its special keys Stop,again etc..
1050 So this little patch enables F11 and F12 to work as expected
1051 following link has documentation on it:
1052 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4734408
1053 also from /usr/include/X11/Sunkeysym.h
1054 #define SunXK_F36 0x1005FF10 // Labeled F11
1055 #define SunXK_F37 0x1005FF11 // Labeled F12
1060 } else if (key == 268828432) {
1061 type = GHOST_kKeyF11;
1062 } else if (key == 268828433) {
1063 type = GHOST_kKeyF12;
1067 GXMAP(type,XK_BackSpace, GHOST_kKeyBackSpace);
1068 GXMAP(type,XK_Tab, GHOST_kKeyTab);
1069 GXMAP(type,XK_Return, GHOST_kKeyEnter);
1070 GXMAP(type,XK_Escape, GHOST_kKeyEsc);
1071 GXMAP(type,XK_space, GHOST_kKeySpace);
1073 GXMAP(type,XK_Linefeed, GHOST_kKeyLinefeed);
1074 GXMAP(type,XK_semicolon, GHOST_kKeySemicolon);
1075 GXMAP(type,XK_period, GHOST_kKeyPeriod);
1076 GXMAP(type,XK_comma, GHOST_kKeyComma);
1077 GXMAP(type,XK_quoteright, GHOST_kKeyQuote);
1078 GXMAP(type,XK_quoteleft, GHOST_kKeyAccentGrave);
1079 GXMAP(type,XK_minus, GHOST_kKeyMinus);
1080 GXMAP(type,XK_slash, GHOST_kKeySlash);
1081 GXMAP(type,XK_backslash, GHOST_kKeyBackslash);
1082 GXMAP(type,XK_equal, GHOST_kKeyEqual);
1083 GXMAP(type,XK_bracketleft, GHOST_kKeyLeftBracket);
1084 GXMAP(type,XK_bracketright, GHOST_kKeyRightBracket);
1085 GXMAP(type,XK_Pause, GHOST_kKeyPause);
1087 GXMAP(type,XK_Shift_L, GHOST_kKeyLeftShift);
1088 GXMAP(type,XK_Shift_R, GHOST_kKeyRightShift);
1089 GXMAP(type,XK_Control_L, GHOST_kKeyLeftControl);
1090 GXMAP(type,XK_Control_R, GHOST_kKeyRightControl);
1091 GXMAP(type,XK_Alt_L, GHOST_kKeyLeftAlt);
1092 GXMAP(type,XK_Alt_R, GHOST_kKeyRightAlt);
1094 GXMAP(type,XK_Insert, GHOST_kKeyInsert);
1095 GXMAP(type,XK_Delete, GHOST_kKeyDelete);
1096 GXMAP(type,XK_Home, GHOST_kKeyHome);
1097 GXMAP(type,XK_End, GHOST_kKeyEnd);
1098 GXMAP(type,XK_Page_Up, GHOST_kKeyUpPage);
1099 GXMAP(type,XK_Page_Down, GHOST_kKeyDownPage);
1101 GXMAP(type,XK_Left, GHOST_kKeyLeftArrow);
1102 GXMAP(type,XK_Right, GHOST_kKeyRightArrow);
1103 GXMAP(type,XK_Up, GHOST_kKeyUpArrow);
1104 GXMAP(type,XK_Down, GHOST_kKeyDownArrow);
1106 GXMAP(type,XK_Caps_Lock, GHOST_kKeyCapsLock);
1107 GXMAP(type,XK_Scroll_Lock, GHOST_kKeyScrollLock);
1108 GXMAP(type,XK_Num_Lock, GHOST_kKeyNumLock);
1112 GXMAP(type,XK_KP_0, GHOST_kKeyNumpad0);
1113 GXMAP(type,XK_KP_1, GHOST_kKeyNumpad1);
1114 GXMAP(type,XK_KP_2, GHOST_kKeyNumpad2);
1115 GXMAP(type,XK_KP_3, GHOST_kKeyNumpad3);
1116 GXMAP(type,XK_KP_4, GHOST_kKeyNumpad4);
1117 GXMAP(type,XK_KP_5, GHOST_kKeyNumpad5);
1118 GXMAP(type,XK_KP_6, GHOST_kKeyNumpad6);
1119 GXMAP(type,XK_KP_7, GHOST_kKeyNumpad7);
1120 GXMAP(type,XK_KP_8, GHOST_kKeyNumpad8);
1121 GXMAP(type,XK_KP_9, GHOST_kKeyNumpad9);
1122 GXMAP(type,XK_KP_Decimal, GHOST_kKeyNumpadPeriod);
1124 GXMAP(type,XK_KP_Insert, GHOST_kKeyNumpad0);
1125 GXMAP(type,XK_KP_End, GHOST_kKeyNumpad1);
1126 GXMAP(type,XK_KP_Down, GHOST_kKeyNumpad2);
1127 GXMAP(type,XK_KP_Page_Down, GHOST_kKeyNumpad3);
1128 GXMAP(type,XK_KP_Left, GHOST_kKeyNumpad4);
1129 GXMAP(type,XK_KP_Begin, GHOST_kKeyNumpad5);
1130 GXMAP(type,XK_KP_Right, GHOST_kKeyNumpad6);
1131 GXMAP(type,XK_KP_Home, GHOST_kKeyNumpad7);
1132 GXMAP(type,XK_KP_Up, GHOST_kKeyNumpad8);
1133 GXMAP(type,XK_KP_Page_Up, GHOST_kKeyNumpad9);
1134 GXMAP(type,XK_KP_Delete, GHOST_kKeyNumpadPeriod);
1136 GXMAP(type,XK_KP_Enter, GHOST_kKeyNumpadEnter);
1137 GXMAP(type,XK_KP_Add, GHOST_kKeyNumpadPlus);
1138 GXMAP(type,XK_KP_Subtract, GHOST_kKeyNumpadMinus);
1139 GXMAP(type,XK_KP_Multiply, GHOST_kKeyNumpadAsterisk);
1140 GXMAP(type,XK_KP_Divide, GHOST_kKeyNumpadSlash);
1142 /* some extra sun cruft (NICE KEYBOARD!) */
1144 GXMAP(type,0xffde, GHOST_kKeyNumpad1);
1145 GXMAP(type,0xffe0, GHOST_kKeyNumpad3);
1146 GXMAP(type,0xffdc, GHOST_kKeyNumpad5);
1147 GXMAP(type,0xffd8, GHOST_kKeyNumpad7);
1148 GXMAP(type,0xffda, GHOST_kKeyNumpad9);
1150 GXMAP(type,0xffd6, GHOST_kKeyNumpadSlash);
1151 GXMAP(type,0xffd7, GHOST_kKeyNumpadAsterisk);
1155 type = GHOST_kKeyUnknown;
1165 /* from xclip.c xcout() v0.11 */
1167 #define XCLIB_XCOUT_NONE 0 /* no context */
1168 #define XCLIB_XCOUT_SENTCONVSEL 1 /* sent a request */
1169 #define XCLIB_XCOUT_INCR 2 /* in an incr loop */
1170 #define XCLIB_XCOUT_FALLBACK 3 /* STRING failed, need fallback to UTF8 */
1171 #define XCLIB_XCOUT_FALLBACK_UTF8 4 /* UTF8 failed, move to compouned */
1172 #define XCLIB_XCOUT_FALLBACK_COMP 5 /* compouned failed, move to text. */
1173 #define XCLIB_XCOUT_FALLBACK_TEXT 6
1175 // Retrieves the contents of a selections.
1176 void GHOST_SystemX11::getClipboard_xcout(XEvent evt,
1177 Atom sel, Atom target, unsigned char **txt,
1178 unsigned long *len, unsigned int *context) const
1182 unsigned char *buffer;
1183 unsigned long pty_size, pty_items;
1184 unsigned char *ltxt= *txt;
1186 vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
1187 vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1188 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1189 Window win = window->getXWindow();
1192 // There is no context, do an XConvertSelection()
1193 case XCLIB_XCOUT_NONE:
1194 // Initialise return length to 0
1200 // Send a selection request
1201 XConvertSelection(m_display, sel, target, m_xclip_out, win, CurrentTime);
1202 *context = XCLIB_XCOUT_SENTCONVSEL;
1205 case XCLIB_XCOUT_SENTCONVSEL:
1206 if (evt.type != SelectionNotify)
1209 if (target == m_utf8_string && evt.xselection.property == None) {
1210 *context= XCLIB_XCOUT_FALLBACK_UTF8;
1213 else if (target == m_compound_text && evt.xselection.property == None) {
1214 *context= XCLIB_XCOUT_FALLBACK_COMP;
1217 else if (target == m_text && evt.xselection.property == None) {
1218 *context= XCLIB_XCOUT_FALLBACK_TEXT;
1222 // find the size and format of the data in property
1223 XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False,
1224 AnyPropertyType, &pty_type, &pty_format,
1225 &pty_items, &pty_size, &buffer);
1228 if (pty_type == m_incr) {
1229 // start INCR mechanism by deleting property
1230 XDeleteProperty(m_display, win, m_xclip_out);
1232 *context = XCLIB_XCOUT_INCR;
1236 // if it's not incr, and not format == 8, then there's
1237 // nothing in the selection (that xclip understands, anyway)
1239 if (pty_format != 8) {
1240 *context = XCLIB_XCOUT_NONE;
1244 // not using INCR mechanism, just read the property
1245 XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size,
1246 False, AnyPropertyType, &pty_type,
1247 &pty_format, &pty_items, &pty_size, &buffer);
1249 // finished with property, delete it
1250 XDeleteProperty(m_display, win, m_xclip_out);
1252 // copy the buffer to the pointer for returned data
1253 ltxt = (unsigned char *) malloc(pty_items);
1254 memcpy(ltxt, buffer, pty_items);
1256 // set the length of the returned data
1263 *context = XCLIB_XCOUT_NONE;
1265 // complete contents of selection fetched, return 1
1268 case XCLIB_XCOUT_INCR:
1269 // To use the INCR method, we basically delete the
1270 // property with the selection in it, wait for an
1271 // event indicating that the property has been created,
1272 // then read it, delete it, etc.
1274 // make sure that the event is relevant
1275 if (evt.type != PropertyNotify)
1278 // skip unless the property has a new value
1279 if (evt.xproperty.state != PropertyNewValue)
1282 // check size and format of the property
1283 XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False,
1284 AnyPropertyType, &pty_type, &pty_format,
1285 &pty_items, &pty_size, (unsigned char **) &buffer);
1287 if (pty_format != 8) {
1288 // property does not contain text, delete it
1289 // to tell the other X client that we have read
1290 // it and to send the next property
1292 XDeleteProperty(m_display, win, m_xclip_out);
1296 if (pty_size == 0) {
1297 // no more data, exit from loop
1299 XDeleteProperty(m_display, win, m_xclip_out);
1300 *context = XCLIB_XCOUT_NONE;
1302 // this means that an INCR transfer is now
1303 // complete, return 1
1309 // if we have come this far, the propery contains
1310 // text, we know the size.
1311 XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size,
1312 False, AnyPropertyType, &pty_type, &pty_format,
1313 &pty_items, &pty_size, (unsigned char **) &buffer);
1315 // allocate memory to accommodate data in *txt
1318 ltxt = (unsigned char *) malloc(*len);
1322 ltxt = (unsigned char *) realloc(ltxt, *len);
1326 memcpy(<xt[*len - pty_items], buffer, pty_items);
1331 // delete property to get the next item
1332 XDeleteProperty(m_display, win, m_xclip_out);
1339 GHOST_TUns8 *GHOST_SystemX11::getClipboard(bool selection) const
1342 Atom target= m_string;
1345 // from xclip.c doOut() v0.11
1346 unsigned char *sel_buf;
1347 unsigned long sel_len= 0;
1349 unsigned int context= XCLIB_XCOUT_NONE;
1351 if (selection == True)
1356 vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
1357 vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1358 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1359 Window win = window->getXWindow();
1361 /* check if we are the owner. */
1362 owner= XGetSelectionOwner(m_display, sseln);
1364 if (sseln == m_clipboard) {
1365 sel_buf= (unsigned char *)malloc(strlen(txt_cut_buffer)+1);
1366 strcpy((char *)sel_buf, txt_cut_buffer);
1367 return((GHOST_TUns8*)sel_buf);
1370 sel_buf= (unsigned char *)malloc(strlen(txt_select_buffer)+1);
1371 strcpy((char *)sel_buf, txt_select_buffer);
1372 return((GHOST_TUns8*)sel_buf);
1375 else if (owner == None)
1379 /* only get an event if xcout() is doing something */
1380 if (context != XCLIB_XCOUT_NONE)
1381 XNextEvent(m_display, &evt);
1383 /* fetch the selection, or part of it */
1384 getClipboard_xcout(evt, sseln, target, &sel_buf, &sel_len, &context);
1386 /* fallback is needed. set XA_STRING to target and restart the loop. */
1387 if (context == XCLIB_XCOUT_FALLBACK) {
1388 context= XCLIB_XCOUT_NONE;
1392 else if (context == XCLIB_XCOUT_FALLBACK_UTF8) {
1393 /* utf8 fail, move to compouned text. */
1394 context= XCLIB_XCOUT_NONE;
1395 target= m_compound_text;
1398 else if (context == XCLIB_XCOUT_FALLBACK_COMP) {
1399 /* compouned text faile, move to text. */
1400 context= XCLIB_XCOUT_NONE;
1405 /* only continue if xcout() is doing something */
1406 if (context == XCLIB_XCOUT_NONE)
1411 /* only print the buffer out, and free it, if it's not
1414 unsigned char *tmp_data = (unsigned char*) malloc(sel_len+1);
1415 memcpy((char*)tmp_data, (char*)sel_buf, sel_len);
1416 tmp_data[sel_len] = '\0';
1418 if (sseln == m_string)
1423 return (GHOST_TUns8*)tmp_data;
1428 void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1430 Window m_window, owner;
1432 vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
1433 vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1434 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1435 m_window = window->getXWindow();
1438 if (selection == False) {
1439 XSetSelectionOwner(m_display, m_clipboard, m_window, CurrentTime);
1440 owner= XGetSelectionOwner(m_display, m_clipboard);
1442 free((void*)txt_cut_buffer);
1444 txt_cut_buffer = (char*) malloc(strlen(buffer)+1);
1445 strcpy(txt_cut_buffer, buffer);
1447 XSetSelectionOwner(m_display, m_primary, m_window, CurrentTime);
1448 owner= XGetSelectionOwner(m_display, m_primary);
1449 if (txt_select_buffer)
1450 free((void*)txt_select_buffer);
1452 txt_select_buffer = (char*) malloc(strlen(buffer)+1);
1453 strcpy(txt_select_buffer, buffer);
1456 if (owner != m_window)
1457 fprintf(stderr, "failed to own primary\n");