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 *****
32 /** \file ghost/intern/GHOST_SystemX11.cpp
37 #include "GHOST_SystemX11.h"
38 #include "GHOST_WindowX11.h"
39 #include "GHOST_WindowManager.h"
40 #include "GHOST_TimerManager.h"
41 #include "GHOST_EventCursor.h"
42 #include "GHOST_EventKey.h"
43 #include "GHOST_EventButton.h"
44 #include "GHOST_EventWheel.h"
45 #include "GHOST_EventNDOF.h"
46 #include "GHOST_NDOFManager.h"
47 #include "GHOST_DisplayManagerX11.h"
49 #include "GHOST_Debug.h"
51 #include <X11/Xatom.h>
52 #include <X11/keysym.h>
53 #include <X11/XKBlib.h> /* allow detectable autorepeate */
55 #ifdef WITH_XF86KEYSYM
56 #include <X11/XF86keysym.h>
61 #if defined(_SGI_EXTRA_PREDEFINES) && !defined(NO_FAST_ATOMS)
62 #include <X11/SGIFastAtom.h>
64 #define XSGIFastInternAtom(dpy,string,fast_name,how) XInternAtom(dpy,string,how)
76 #include <stdio.h> // for fprintf only
77 #include <cstdlib> // for exit
80 convertXKey(KeySym key);
82 typedef struct NDOFPlatformInfo {
85 volatile GHOST_TEventNDOFData *currValues;
92 static NDOFPlatformInfo sNdofInfo = {NULL, 0, NULL, 0, 0, 0, 0};
95 //these are for copy and select copy
96 static char *txt_cut_buffer= NULL;
97 static char *txt_select_buffer= NULL;
107 m_display = XOpenDisplay(NULL);
110 std::cerr << "Unable to open a display" << std::endl;
111 abort(); //was return before, but this would just mean it will crash later
116 = XSGIFastInternAtom(m_display,
118 SGI_XA_WM_DELETE_WINDOW, False);
121 = XInternAtom(m_display, "WM_DELETE_WINDOW", True);
124 m_wm_protocols= XInternAtom(m_display, "WM_PROTOCOLS", False);
125 m_wm_take_focus= XInternAtom(m_display, "WM_TAKE_FOCUS", False);
126 m_wm_state= XInternAtom(m_display, "WM_STATE", False);
127 m_wm_change_state= XInternAtom(m_display, "WM_CHANGE_STATE", False);
128 m_net_state= XInternAtom(m_display, "_NET_WM_STATE", False);
129 m_net_max_horz= XInternAtom(m_display,
130 "_NET_WM_STATE_MAXIMIZED_HORZ", False);
131 m_net_max_vert= XInternAtom(m_display,
132 "_NET_WM_STATE_MAXIMIZED_VERT", False);
133 m_net_fullscreen= XInternAtom(m_display,
134 "_NET_WM_STATE_FULLSCREEN", False);
135 m_motif= XInternAtom(m_display, "_MOTIF_WM_HINTS", False);
136 m_targets= XInternAtom(m_display, "TARGETS", False);
137 m_string= XInternAtom(m_display, "STRING", False);
138 m_compound_text= XInternAtom(m_display, "COMPOUND_TEXT", False);
139 m_text= XInternAtom(m_display, "TEXT", False);
140 m_clipboard= XInternAtom(m_display, "CLIPBOARD", False);
141 m_primary= XInternAtom(m_display, "PRIMARY", False);
142 m_xclip_out= XInternAtom(m_display, "XCLIP_OUT", False);
143 m_incr= XInternAtom(m_display, "INCR", False);
144 m_utf8_string= XInternAtom(m_display, "UTF8_STRING", False);
148 // compute the initial time
150 if (gettimeofday(&tv,NULL) == -1) {
151 GHOST_ASSERT(false,"Could not instantiate timer!");
154 m_start_time = GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000);
157 /* use detectable autorepeate, mac and windows also do this */
159 int xkb_opcode, xkb_event, xkb_error;
160 int xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion;
162 use_xkb = XkbQueryExtension(m_display, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
164 XkbSetDetectableAutoRepeat(m_display, true, NULL);
172 XCloseDisplay(m_display);
180 GHOST_TSuccess success = GHOST_System::init();
183 m_displayManager = new GHOST_DisplayManagerX11(this);
185 if (m_displayManager) {
186 return GHOST_kSuccess;
190 return GHOST_kFailure;
198 if (gettimeofday(&tv,NULL) == -1) {
199 GHOST_ASSERT(false,"Could not compute time!");
202 return GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000) - m_start_time;
209 return GHOST_TUns8(1);
213 * Returns the dimensions of the main display on this system.
214 * @return The dimension of the main display.
218 getMainDisplayDimensions(
223 width = DisplayWidth(m_display, DefaultScreen(m_display));
224 height = DisplayHeight(m_display, DefaultScreen(m_display));
229 * Create a new window.
230 * The new window is added to the list of windows managed.
231 * Never explicitly delete the window, use disposeWindow() instead.
232 * @param title The name of the window (displayed in the title bar of the window if the OS supports it).
233 * @param left The coordinate of the left edge of the window.
234 * @param top The coordinate of the top edge of the window.
235 * @param width The width the window.
236 * @param height The height the window.
237 * @param state The state of the window when opened.
238 * @param type The type of drawing context installed in this window.
239 * @param stereoVisual Stereo visual for quad buffered stereo.
240 * @param numOfAASamples Number of samples used for AA (zero if no AA)
241 * @param parentWindow Parent (embedder) window
242 * @return The new window (or 0 if creation failed).
247 const STR_String& title,
252 GHOST_TWindowState state,
253 GHOST_TDrawingContextType type,
255 const GHOST_TUns16 numOfAASamples,
256 const GHOST_TEmbedderWindowID parentWindow
258 GHOST_WindowX11 * window = 0;
260 if (!m_display) return 0;
265 window = new GHOST_WindowX11 (
266 this,m_display,title, left, top, width, height, state, parentWindow, type, stereoVisual
270 // Both are now handle in GHOST_WindowX11.cpp
271 // Focus and Delete atoms.
273 if (window->getValid()) {
274 // Store the pointer to the window
275 m_windowManager->addWindow(window);
277 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
293 if (xwind == 0) return NULL;
295 // It is not entirely safe to do this as the backptr may point
296 // to a window that has recently been removed.
297 // We should always check the window manager's list of windows
298 // and only process events on these windows.
300 vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
302 vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
303 vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
305 for (; win_it != win_end; ++win_it) {
306 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
307 if (window->getXWindow() == xwind) {
315 static void SleepTillEvent(Display *display, GHOST_TInt64 maxSleep) {
316 int fd = ConnectionNumber(display);
322 if (maxSleep == -1) {
323 select(fd + 1, &fds, NULL, NULL, NULL);
327 tv.tv_sec = maxSleep/1000;
328 tv.tv_usec = (maxSleep - tv.tv_sec*1000)*1000;
330 select(fd + 1, &fds, NULL, NULL, &tv);
334 /* This function borrowed from Qt's X11 support
337 struct init_timestamp_data
342 static Bool init_timestamp_scanner(Display*, XEvent *event, XPointer arg)
344 init_timestamp_data *data =
345 reinterpret_cast<init_timestamp_data*>(arg);
350 data->timestamp = event->xbutton.time;
353 data->timestamp = event->xmotion.time;
357 data->timestamp = event->xkey.time;
360 data->timestamp = event->xproperty.time;
364 data->timestamp = event->xcrossing.time;
367 data->timestamp = event->xselectionclear.time;
378 lastEventTime(Time default_time) {
379 init_timestamp_data data;
380 data.timestamp = default_time;
382 XCheckIfEvent(m_display, &ev, &init_timestamp_scanner, (XPointer)&data);
384 return data.timestamp;
394 // Get all the current events -- translate them into
395 // ghost events and call base class pushEvent() method.
397 bool anyProcessed = false;
400 GHOST_TimerManager* timerMgr = getTimerManager();
402 if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
403 GHOST_TUns64 next = timerMgr->nextFireTime();
405 if (next==GHOST_kFireTimeNever) {
406 SleepTillEvent(m_display, -1);
408 GHOST_TInt64 maxSleep = next - getMilliSeconds();
411 SleepTillEvent(m_display, next - getMilliSeconds());
415 if (timerMgr->fireTimers(getMilliSeconds())) {
419 while (XPending(m_display)) {
421 XNextEvent(m_display, &xevent);
422 processEvent(&xevent);
426 if (generateWindowExposeEvents()) {
429 } while (waitForEvent && !anyProcessed);
435 GHOST_SystemX11::processEvent(XEvent *xe)
437 GHOST_WindowX11 * window = findGhostWindow(xe->xany.window);
438 GHOST_Event * g_event = NULL;
447 XExposeEvent & xee = xe->xexpose;
449 if (xee.count == 0) {
450 // Only generate a single expose event
451 // per read of the event queue.
456 GHOST_kEventWindowUpdate,
465 XMotionEvent &xme = xe->xmotion;
467 if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
469 GHOST_TInt32 x_new= xme.x_root;
470 GHOST_TInt32 y_new= xme.y_root;
471 GHOST_TInt32 x_accum, y_accum;
474 /* fallback to window bounds */
475 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
476 window->getClientBounds(bounds);
478 /* could also clamp to screen bounds
479 * wrap with a window outside the view will fail atm */
480 bounds.wrapPoint(x_new, y_new, 8); /* offset of one incase blender is at screen bounds */
481 window->getCursorGrabAccum(x_accum, y_accum);
483 if(x_new != xme.x_root || y_new != xme.y_root) {
484 if (xme.time > m_last_warp) {
485 /* when wrapping we don't need to add an event because the
486 * setCursorPosition call will cause a new event after */
487 setCursorPosition(x_new, y_new); /* wrap */
488 window->setCursorGrabAccum(x_accum + (xme.x_root - x_new), y_accum + (xme.y_root - y_new));
489 m_last_warp = lastEventTime(xme.time);
491 setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
498 GHOST_kEventCursorMove,
500 xme.x_root + x_accum,
509 GHOST_kEventCursorMove,
521 XKeyEvent *xke = &(xe->xkey);
523 KeySym key_sym = XLookupKeysym(xke,0);
526 GHOST_TKey gkey = convertXKey(key_sym);
527 GHOST_TEventType type = (xke->type == KeyPress) ?
528 GHOST_kEventKeyDown : GHOST_kEventKeyUp;
530 if (!XLookupString(xke, &ascii, 1, NULL, NULL)) {
549 XButtonEvent & xbe = xe->xbutton;
550 GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
551 GHOST_TEventType type = (xbe.type == ButtonPress) ?
552 GHOST_kEventButtonDown : GHOST_kEventButtonUp;
554 /* process wheel mouse events and break, only pass on press events */
555 if(xbe.button == Button4) {
556 if(xbe.type == ButtonPress)
557 g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1);
560 else if(xbe.button == Button5) {
561 if(xbe.type == ButtonPress)
562 g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1);
566 /* process rest of normal mouse buttons */
567 if(xbe.button == Button1)
568 gbmask = GHOST_kButtonMaskLeft;
569 else if(xbe.button == Button2)
570 gbmask = GHOST_kButtonMaskMiddle;
571 else if(xbe.button == Button3)
572 gbmask = GHOST_kButtonMaskRight;
573 /* It seems events 6 and 7 are for horizontal scrolling.
574 * you can re-order button mapping like this... (swaps 6,7 with 8,9)
575 * xmodmap -e "pointer = 1 2 3 4 5 8 9 6 7"
577 else if(xbe.button == 8)
578 gbmask = GHOST_kButtonMaskButton4;
579 else if(xbe.button == 9)
580 gbmask = GHOST_kButtonMaskButton5;
594 // change of size, border, layer etc.
595 case ConfigureNotify:
597 /* XConfigureEvent & xce = xe->xconfigure; */
602 GHOST_kEventWindowSize,
611 XFocusChangeEvent &xfe = xe->xfocus;
613 // May have to look at the type of event and filter some
616 GHOST_TEventType gtype = (xfe.type == FocusIn) ?
617 GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate;
630 XClientMessageEvent & xcme = xe->xclient;
633 if (((Atom)xcme.data.l[0]) == m_delete_window_atom) {
637 GHOST_kEventWindowClose,
642 if (sNdofInfo.currValues) {
643 static GHOST_TEventNDOFData data = {0,0,0,0,0,0,0,0,0,0,0};
644 if (xcme.message_type == sNdofInfo.motionAtom)
647 data.delta = xcme.data.s[8] - data.time;
648 data.time = xcme.data.s[8];
649 data.tx = xcme.data.s[2] >> 2;
650 data.ty = xcme.data.s[3] >> 2;
651 data.tz = xcme.data.s[4] >> 2;
652 data.rx = xcme.data.s[5];
653 data.ry = xcme.data.s[6];
654 data.rz =-xcme.data.s[7];
655 g_event = new GHOST_EventNDOF(getMilliSeconds(),
656 GHOST_kEventNDOFMotion,
658 } else if (xcme.message_type == sNdofInfo.btnPressAtom) {
660 data.delta = xcme.data.s[8] - data.time;
661 data.time = xcme.data.s[8];
662 data.buttons = xcme.data.s[2];
663 g_event = new GHOST_EventNDOF(getMilliSeconds(),
664 GHOST_kEventNDOFButton,
667 } else if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
668 XWindowAttributes attr;
672 /* as ICCCM say, we need reply this event
673 * with a SetInputFocus, the data[1] have
674 * the valid timestamp (send by the wm).
676 * Some WM send this event before the
677 * window is really mapped (for example
678 * change from virtual desktop), so we need
679 * to be sure that our windows is mapped
680 * or this call fail and close blender.
682 if (XGetWindowAttributes(m_display, xcme.window, &attr) == True) {
683 if (XGetInputFocus(m_display, &fwin, &revert_to) == True) {
684 if (attr.map_state == IsViewable) {
685 if (fwin != xcme.window)
686 XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
691 /* Unknown client message, ignore */
698 // We're not interested in the following things.(yet...)
700 case GraphicsExpose :
706 /* XCrossingEvents pointer leave enter window.
707 also do cursor move here, MotionNotify only
708 happens when motion starts & ends inside window.
709 we only do moves when the crossing mode is 'normal'
710 (really crossing between windows) since some windowmanagers
711 also send grab/ungrab crossings for mousewheel events.
713 XCrossingEvent &xce = xe->xcrossing;
714 if( xce.mode == NotifyNormal ) {
718 GHOST_kEventCursorMove,
729 * [ Clients can select for StructureNotify on their
730 * top-level windows to track transition between
731 * Normal and Iconic states. Receipt of a MapNotify
732 * event will indicate a transition to the Normal
733 * state, and receipt of an UnmapNotify event will
734 * indicate a transition to the Iconic state. ]
736 if (window->m_post_init == True) {
738 * Now we are sure that the window is
739 * mapped, so only need change the state.
741 window->setState (window->m_post_state);
742 window->m_post_init = False;
750 case SelectionRequest:
753 Atom target, string, compound_text, c_string;
754 XSelectionRequestEvent *xse = &xe->xselectionrequest;
756 target = XInternAtom(m_display, "TARGETS", False);
757 string = XInternAtom(m_display, "STRING", False);
758 compound_text = XInternAtom(m_display, "COMPOUND_TEXT", False);
759 c_string = XInternAtom(m_display, "C_STRING", False);
761 /* support obsolete clients */
762 if (xse->property == None) {
763 xse->property = xse->target;
766 nxe.xselection.type = SelectionNotify;
767 nxe.xselection.requestor = xse->requestor;
768 nxe.xselection.property = xse->property;
769 nxe.xselection.display = xse->display;
770 nxe.xselection.selection = xse->selection;
771 nxe.xselection.target = xse->target;
772 nxe.xselection.time = xse->time;
774 /*Check to see if the requestor is asking for String*/
775 if(xse->target == string || xse->target == compound_text || xse->target == c_string) {
776 if (xse->selection == XInternAtom(m_display, "PRIMARY", False)) {
777 XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 8, PropModeReplace, (unsigned char*)txt_select_buffer, strlen(txt_select_buffer));
778 } else if (xse->selection == XInternAtom(m_display, "CLIPBOARD", False)) {
779 XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 8, PropModeReplace, (unsigned char*)txt_cut_buffer, strlen(txt_cut_buffer));
781 } else if (xse->target == target) {
785 alist[2] = compound_text;
787 XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 32, PropModeReplace, (unsigned char*)alist, 4);
790 //Change property to None because we do not support anything but STRING
791 nxe.xselection.property = None;
794 //Send the event to the client 0 0 == False, SelectionNotify
795 XSendEvent(m_display, xse->requestor, 0, 0, &nxe);
801 #ifdef WITH_X11_XINPUT
802 if(xe->type == window->GetXTablet().MotionEvent)
804 XDeviceMotionEvent* data = (XDeviceMotionEvent*)xe;
805 window->GetXTablet().CommonData.Pressure=
806 data->axis_data[2]/((float)window->GetXTablet().PressureLevels);
808 /* the (short) cast and the &0xffff is bizarre and unexplained anywhere,
809 * but I got garbage data without it. Found it in the xidump.c source --matt */
810 window->GetXTablet().CommonData.Xtilt=
811 (short)(data->axis_data[3]&0xffff)/((float)window->GetXTablet().XtiltLevels);
812 window->GetXTablet().CommonData.Ytilt=
813 (short)(data->axis_data[4]&0xffff)/((float)window->GetXTablet().YtiltLevels);
815 else if(xe->type == window->GetXTablet().ProxInEvent)
817 XProximityNotifyEvent* data = (XProximityNotifyEvent*)xe;
818 if(data->deviceid == window->GetXTablet().StylusID)
819 window->GetXTablet().CommonData.Active= GHOST_kTabletModeStylus;
820 else if(data->deviceid == window->GetXTablet().EraserID)
821 window->GetXTablet().CommonData.Active= GHOST_kTabletModeEraser;
823 else if(xe->type == window->GetXTablet().ProxOutEvent)
824 window->GetXTablet().CommonData.Active= GHOST_kTabletModeNone;
825 #endif // WITH_X11_XINPUT
837 prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
839 const vector<GHOST_IWindow*>& v(m_windowManager->getWindows());
841 sNdofInfo.window = static_cast<GHOST_WindowX11*>(v[0])->getXWindow();
842 sNdofInfo.display = m_display;
843 sNdofInfo.currValues = currentNdofValues;
844 return (void*)&sNdofInfo;
850 GHOST_ModifierKeys& keys
853 // analyse the masks retuned from XQueryPointer.
855 memset((void *)m_keyboard_vector,0,sizeof(m_keyboard_vector));
857 XQueryKeymap(m_display,(char *)m_keyboard_vector);
859 // now translate key symobols into keycodes and
862 const static KeyCode shift_l = XKeysymToKeycode(m_display,XK_Shift_L);
863 const static KeyCode shift_r = XKeysymToKeycode(m_display,XK_Shift_R);
864 const static KeyCode control_l = XKeysymToKeycode(m_display,XK_Control_L);
865 const static KeyCode control_r = XKeysymToKeycode(m_display,XK_Control_R);
866 const static KeyCode alt_l = XKeysymToKeycode(m_display,XK_Alt_L);
867 const static KeyCode alt_r = XKeysymToKeycode(m_display,XK_Alt_R);
868 const static KeyCode super_l = XKeysymToKeycode(m_display,XK_Super_L);
869 const static KeyCode super_r = XKeysymToKeycode(m_display,XK_Super_R);
872 keys.set(GHOST_kModifierKeyLeftShift, ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) != 0);
873 keys.set(GHOST_kModifierKeyRightShift, ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) != 0);
875 keys.set(GHOST_kModifierKeyLeftControl, ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) != 0);
876 keys.set(GHOST_kModifierKeyRightControl, ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) != 0);
878 keys.set(GHOST_kModifierKeyLeftAlt, ((m_keyboard_vector[alt_l >> 3] >> (alt_l & 7)) & 1) != 0);
879 keys.set(GHOST_kModifierKeyRightAlt, ((m_keyboard_vector[alt_r >> 3] >> (alt_r & 7)) & 1) != 0);
880 // super (windows) - only one GHOST-kModifierKeyOS, so mapping to either
881 keys.set(GHOST_kModifierKeyOS, ( ((m_keyboard_vector[super_l >> 3] >> (super_l & 7)) & 1) ||
882 ((m_keyboard_vector[super_r >> 3] >> (super_r & 7)) & 1) ) != 0);
884 return GHOST_kSuccess;
890 GHOST_Buttons& buttons
893 Window root_return, child_return;
895 unsigned int mask_return;
897 if (XQueryPointer(m_display,
898 RootWindow(m_display,DefaultScreen(m_display)),
903 &mask_return) == True)
905 buttons.set(GHOST_kButtonMaskLeft, (mask_return & Button1Mask) != 0);
906 buttons.set(GHOST_kButtonMaskMiddle, (mask_return & Button2Mask) != 0);
907 buttons.set(GHOST_kButtonMaskRight, (mask_return & Button3Mask) != 0);
910 return GHOST_kFailure;
913 return GHOST_kSuccess;
924 Window root_return, child_return;
926 unsigned int mask_return;
930 RootWindow(m_display,DefaultScreen(m_display)),
937 return GHOST_kFailure;
942 return GHOST_kSuccess;
953 // This is a brute force move in screen coordinates
954 // XWarpPointer does relative moves so first determine the
955 // current pointer position.
958 if (getCursorPosition(cx,cy) == GHOST_kFailure) {
959 return GHOST_kFailure;
965 XWarpPointer(m_display,None,None,0,0,0,0,relx,rely);
966 XSync(m_display, 0); /* Sync to process all requests */
968 return GHOST_kSuccess;
975 GHOST_WindowX11 * bad_wind
978 GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)");
980 m_dirty_windows.push_back(bad_wind);
986 generateWindowExposeEvents(
989 vector<GHOST_WindowX11 *>::iterator w_start = m_dirty_windows.begin();
990 vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end();
991 bool anyProcessed = false;
993 for (;w_start != w_end; ++w_start) {
994 GHOST_Event * g_event = new
997 GHOST_kEventWindowUpdate,
1001 (*w_start)->validate();
1005 anyProcessed = true;
1009 m_dirty_windows.clear();
1010 return anyProcessed;
1013 #define GXMAP(k,x,y) case x: k = y; break;
1016 convertXKey(KeySym key)
1020 if ((key >= XK_A) && (key <= XK_Z)) {
1021 type = GHOST_TKey( key - XK_A + int(GHOST_kKeyA));
1022 } else if ((key >= XK_a) && (key <= XK_z)) {
1023 type = GHOST_TKey(key - XK_a + int(GHOST_kKeyA));
1024 } else if ((key >= XK_0) && (key <= XK_9)) {
1025 type = GHOST_TKey(key - XK_0 + int(GHOST_kKey0));
1026 } else if ((key >= XK_F1) && (key <= XK_F24)) {
1027 type = GHOST_TKey(key - XK_F1 + int(GHOST_kKeyF1));
1028 #if defined(__sun) || defined(__sun__)
1029 /* This is a bit of a hack, but it looks like sun
1030 Used F11 and friends for its special keys Stop,again etc..
1031 So this little patch enables F11 and F12 to work as expected
1032 following link has documentation on it:
1033 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4734408
1034 also from /usr/include/X11/Sunkeysym.h
1035 #define SunXK_F36 0x1005FF10 // Labeled F11
1036 #define SunXK_F37 0x1005FF11 // Labeled F12
1041 } else if (key == 268828432) {
1042 type = GHOST_kKeyF11;
1043 } else if (key == 268828433) {
1044 type = GHOST_kKeyF12;
1048 GXMAP(type,XK_BackSpace, GHOST_kKeyBackSpace);
1049 GXMAP(type,XK_Tab, GHOST_kKeyTab);
1050 GXMAP(type,XK_Return, GHOST_kKeyEnter);
1051 GXMAP(type,XK_Escape, GHOST_kKeyEsc);
1052 GXMAP(type,XK_space, GHOST_kKeySpace);
1054 GXMAP(type,XK_Linefeed, GHOST_kKeyLinefeed);
1055 GXMAP(type,XK_semicolon, GHOST_kKeySemicolon);
1056 GXMAP(type,XK_period, GHOST_kKeyPeriod);
1057 GXMAP(type,XK_comma, GHOST_kKeyComma);
1058 GXMAP(type,XK_quoteright, GHOST_kKeyQuote);
1059 GXMAP(type,XK_quoteleft, GHOST_kKeyAccentGrave);
1060 GXMAP(type,XK_minus, GHOST_kKeyMinus);
1061 GXMAP(type,XK_slash, GHOST_kKeySlash);
1062 GXMAP(type,XK_backslash, GHOST_kKeyBackslash);
1063 GXMAP(type,XK_equal, GHOST_kKeyEqual);
1064 GXMAP(type,XK_bracketleft, GHOST_kKeyLeftBracket);
1065 GXMAP(type,XK_bracketright, GHOST_kKeyRightBracket);
1066 GXMAP(type,XK_Pause, GHOST_kKeyPause);
1068 GXMAP(type,XK_Shift_L, GHOST_kKeyLeftShift);
1069 GXMAP(type,XK_Shift_R, GHOST_kKeyRightShift);
1070 GXMAP(type,XK_Control_L, GHOST_kKeyLeftControl);
1071 GXMAP(type,XK_Control_R, GHOST_kKeyRightControl);
1072 GXMAP(type,XK_Alt_L, GHOST_kKeyLeftAlt);
1073 GXMAP(type,XK_Alt_R, GHOST_kKeyRightAlt);
1074 GXMAP(type,XK_Super_L, GHOST_kKeyOS);
1075 GXMAP(type,XK_Super_R, GHOST_kKeyOS);
1077 GXMAP(type,XK_Insert, GHOST_kKeyInsert);
1078 GXMAP(type,XK_Delete, GHOST_kKeyDelete);
1079 GXMAP(type,XK_Home, GHOST_kKeyHome);
1080 GXMAP(type,XK_End, GHOST_kKeyEnd);
1081 GXMAP(type,XK_Page_Up, GHOST_kKeyUpPage);
1082 GXMAP(type,XK_Page_Down, GHOST_kKeyDownPage);
1084 GXMAP(type,XK_Left, GHOST_kKeyLeftArrow);
1085 GXMAP(type,XK_Right, GHOST_kKeyRightArrow);
1086 GXMAP(type,XK_Up, GHOST_kKeyUpArrow);
1087 GXMAP(type,XK_Down, GHOST_kKeyDownArrow);
1089 GXMAP(type,XK_Caps_Lock, GHOST_kKeyCapsLock);
1090 GXMAP(type,XK_Scroll_Lock, GHOST_kKeyScrollLock);
1091 GXMAP(type,XK_Num_Lock, GHOST_kKeyNumLock);
1095 GXMAP(type,XK_KP_0, GHOST_kKeyNumpad0);
1096 GXMAP(type,XK_KP_1, GHOST_kKeyNumpad1);
1097 GXMAP(type,XK_KP_2, GHOST_kKeyNumpad2);
1098 GXMAP(type,XK_KP_3, GHOST_kKeyNumpad3);
1099 GXMAP(type,XK_KP_4, GHOST_kKeyNumpad4);
1100 GXMAP(type,XK_KP_5, GHOST_kKeyNumpad5);
1101 GXMAP(type,XK_KP_6, GHOST_kKeyNumpad6);
1102 GXMAP(type,XK_KP_7, GHOST_kKeyNumpad7);
1103 GXMAP(type,XK_KP_8, GHOST_kKeyNumpad8);
1104 GXMAP(type,XK_KP_9, GHOST_kKeyNumpad9);
1105 GXMAP(type,XK_KP_Decimal, GHOST_kKeyNumpadPeriod);
1107 GXMAP(type,XK_KP_Insert, GHOST_kKeyNumpad0);
1108 GXMAP(type,XK_KP_End, GHOST_kKeyNumpad1);
1109 GXMAP(type,XK_KP_Down, GHOST_kKeyNumpad2);
1110 GXMAP(type,XK_KP_Page_Down, GHOST_kKeyNumpad3);
1111 GXMAP(type,XK_KP_Left, GHOST_kKeyNumpad4);
1112 GXMAP(type,XK_KP_Begin, GHOST_kKeyNumpad5);
1113 GXMAP(type,XK_KP_Right, GHOST_kKeyNumpad6);
1114 GXMAP(type,XK_KP_Home, GHOST_kKeyNumpad7);
1115 GXMAP(type,XK_KP_Up, GHOST_kKeyNumpad8);
1116 GXMAP(type,XK_KP_Page_Up, GHOST_kKeyNumpad9);
1117 GXMAP(type,XK_KP_Delete, GHOST_kKeyNumpadPeriod);
1119 GXMAP(type,XK_KP_Enter, GHOST_kKeyNumpadEnter);
1120 GXMAP(type,XK_KP_Add, GHOST_kKeyNumpadPlus);
1121 GXMAP(type,XK_KP_Subtract, GHOST_kKeyNumpadMinus);
1122 GXMAP(type,XK_KP_Multiply, GHOST_kKeyNumpadAsterisk);
1123 GXMAP(type,XK_KP_Divide, GHOST_kKeyNumpadSlash);
1125 /* Media keys in some keyboards and laptops with XFree86/Xorg */
1126 #ifdef WITH_XF86KEYSYM
1127 GXMAP(type,XF86XK_AudioPlay, GHOST_kKeyMediaPlay);
1128 GXMAP(type,XF86XK_AudioStop, GHOST_kKeyMediaStop);
1129 GXMAP(type,XF86XK_AudioPrev, GHOST_kKeyMediaFirst);
1130 GXMAP(type,XF86XK_AudioRewind, GHOST_kKeyMediaFirst);
1131 GXMAP(type,XF86XK_AudioNext, GHOST_kKeyMediaLast);
1132 #ifdef XF86XK_AudioForward /* Debian lenny's XF86keysym.h has no XF86XK_AudioForward define */
1133 GXMAP(type,XF86XK_AudioForward, GHOST_kKeyMediaLast);
1137 /* some extra sun cruft (NICE KEYBOARD!) */
1139 GXMAP(type,0xffde, GHOST_kKeyNumpad1);
1140 GXMAP(type,0xffe0, GHOST_kKeyNumpad3);
1141 GXMAP(type,0xffdc, GHOST_kKeyNumpad5);
1142 GXMAP(type,0xffd8, GHOST_kKeyNumpad7);
1143 GXMAP(type,0xffda, GHOST_kKeyNumpad9);
1145 GXMAP(type,0xffd6, GHOST_kKeyNumpadSlash);
1146 GXMAP(type,0xffd7, GHOST_kKeyNumpadAsterisk);
1150 type = GHOST_kKeyUnknown;
1160 /* from xclip.c xcout() v0.11 */
1162 #define XCLIB_XCOUT_NONE 0 /* no context */
1163 #define XCLIB_XCOUT_SENTCONVSEL 1 /* sent a request */
1164 #define XCLIB_XCOUT_INCR 2 /* in an incr loop */
1165 #define XCLIB_XCOUT_FALLBACK 3 /* STRING failed, need fallback to UTF8 */
1166 #define XCLIB_XCOUT_FALLBACK_UTF8 4 /* UTF8 failed, move to compouned */
1167 #define XCLIB_XCOUT_FALLBACK_COMP 5 /* compouned failed, move to text. */
1168 #define XCLIB_XCOUT_FALLBACK_TEXT 6
1170 // Retrieves the contents of a selections.
1171 void GHOST_SystemX11::getClipboard_xcout(XEvent evt,
1172 Atom sel, Atom target, unsigned char **txt,
1173 unsigned long *len, unsigned int *context) const
1177 unsigned char *buffer;
1178 unsigned long pty_size, pty_items;
1179 unsigned char *ltxt= *txt;
1181 vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
1182 vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1183 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1184 Window win = window->getXWindow();
1187 // There is no context, do an XConvertSelection()
1188 case XCLIB_XCOUT_NONE:
1189 // Initialise return length to 0
1195 // Send a selection request
1196 XConvertSelection(m_display, sel, target, m_xclip_out, win, CurrentTime);
1197 *context = XCLIB_XCOUT_SENTCONVSEL;
1200 case XCLIB_XCOUT_SENTCONVSEL:
1201 if (evt.type != SelectionNotify)
1204 if (target == m_utf8_string && evt.xselection.property == None) {
1205 *context= XCLIB_XCOUT_FALLBACK_UTF8;
1208 else if (target == m_compound_text && evt.xselection.property == None) {
1209 *context= XCLIB_XCOUT_FALLBACK_COMP;
1212 else if (target == m_text && evt.xselection.property == None) {
1213 *context= XCLIB_XCOUT_FALLBACK_TEXT;
1217 // find the size and format of the data in property
1218 XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False,
1219 AnyPropertyType, &pty_type, &pty_format,
1220 &pty_items, &pty_size, &buffer);
1223 if (pty_type == m_incr) {
1224 // start INCR mechanism by deleting property
1225 XDeleteProperty(m_display, win, m_xclip_out);
1227 *context = XCLIB_XCOUT_INCR;
1231 // if it's not incr, and not format == 8, then there's
1232 // nothing in the selection (that xclip understands, anyway)
1234 if (pty_format != 8) {
1235 *context = XCLIB_XCOUT_NONE;
1239 // not using INCR mechanism, just read the property
1240 XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size,
1241 False, AnyPropertyType, &pty_type,
1242 &pty_format, &pty_items, &pty_size, &buffer);
1244 // finished with property, delete it
1245 XDeleteProperty(m_display, win, m_xclip_out);
1247 // copy the buffer to the pointer for returned data
1248 ltxt = (unsigned char *) malloc(pty_items);
1249 memcpy(ltxt, buffer, pty_items);
1251 // set the length of the returned data
1258 *context = XCLIB_XCOUT_NONE;
1260 // complete contents of selection fetched, return 1
1263 case XCLIB_XCOUT_INCR:
1264 // To use the INCR method, we basically delete the
1265 // property with the selection in it, wait for an
1266 // event indicating that the property has been created,
1267 // then read it, delete it, etc.
1269 // make sure that the event is relevant
1270 if (evt.type != PropertyNotify)
1273 // skip unless the property has a new value
1274 if (evt.xproperty.state != PropertyNewValue)
1277 // check size and format of the property
1278 XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False,
1279 AnyPropertyType, &pty_type, &pty_format,
1280 &pty_items, &pty_size, (unsigned char **) &buffer);
1282 if (pty_format != 8) {
1283 // property does not contain text, delete it
1284 // to tell the other X client that we have read
1285 // it and to send the next property
1287 XDeleteProperty(m_display, win, m_xclip_out);
1291 if (pty_size == 0) {
1292 // no more data, exit from loop
1294 XDeleteProperty(m_display, win, m_xclip_out);
1295 *context = XCLIB_XCOUT_NONE;
1297 // this means that an INCR transfer is now
1298 // complete, return 1
1304 // if we have come this far, the propery contains
1305 // text, we know the size.
1306 XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size,
1307 False, AnyPropertyType, &pty_type, &pty_format,
1308 &pty_items, &pty_size, (unsigned char **) &buffer);
1310 // allocate memory to accommodate data in *txt
1313 ltxt = (unsigned char *) malloc(*len);
1317 ltxt = (unsigned char *) realloc(ltxt, *len);
1321 memcpy(<xt[*len - pty_items], buffer, pty_items);
1326 // delete property to get the next item
1327 XDeleteProperty(m_display, win, m_xclip_out);
1334 GHOST_TUns8 *GHOST_SystemX11::getClipboard(bool selection) const
1337 Atom target= m_string;
1340 // from xclip.c doOut() v0.11
1341 unsigned char *sel_buf;
1342 unsigned long sel_len= 0;
1344 unsigned int context= XCLIB_XCOUT_NONE;
1346 if (selection == True)
1351 vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
1352 vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1353 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1354 Window win = window->getXWindow();
1356 /* check if we are the owner. */
1357 owner= XGetSelectionOwner(m_display, sseln);
1359 if (sseln == m_clipboard) {
1360 sel_buf= (unsigned char *)malloc(strlen(txt_cut_buffer)+1);
1361 strcpy((char *)sel_buf, txt_cut_buffer);
1362 return((GHOST_TUns8*)sel_buf);
1365 sel_buf= (unsigned char *)malloc(strlen(txt_select_buffer)+1);
1366 strcpy((char *)sel_buf, txt_select_buffer);
1367 return((GHOST_TUns8*)sel_buf);
1370 else if (owner == None)
1374 /* only get an event if xcout() is doing something */
1375 if (context != XCLIB_XCOUT_NONE)
1376 XNextEvent(m_display, &evt);
1378 /* fetch the selection, or part of it */
1379 getClipboard_xcout(evt, sseln, target, &sel_buf, &sel_len, &context);
1381 /* fallback is needed. set XA_STRING to target and restart the loop. */
1382 if (context == XCLIB_XCOUT_FALLBACK) {
1383 context= XCLIB_XCOUT_NONE;
1387 else if (context == XCLIB_XCOUT_FALLBACK_UTF8) {
1388 /* utf8 fail, move to compouned text. */
1389 context= XCLIB_XCOUT_NONE;
1390 target= m_compound_text;
1393 else if (context == XCLIB_XCOUT_FALLBACK_COMP) {
1394 /* compouned text faile, move to text. */
1395 context= XCLIB_XCOUT_NONE;
1400 /* only continue if xcout() is doing something */
1401 if (context == XCLIB_XCOUT_NONE)
1406 /* only print the buffer out, and free it, if it's not
1409 unsigned char *tmp_data = (unsigned char*) malloc(sel_len+1);
1410 memcpy((char*)tmp_data, (char*)sel_buf, sel_len);
1411 tmp_data[sel_len] = '\0';
1413 if (sseln == m_string)
1418 return (GHOST_TUns8*)tmp_data;
1423 void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1425 Window m_window, owner;
1427 vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
1428 vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
1429 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
1430 m_window = window->getXWindow();
1433 if (selection == False) {
1434 XSetSelectionOwner(m_display, m_clipboard, m_window, CurrentTime);
1435 owner= XGetSelectionOwner(m_display, m_clipboard);
1437 free((void*)txt_cut_buffer);
1439 txt_cut_buffer = (char*) malloc(strlen(buffer)+1);
1440 strcpy(txt_cut_buffer, buffer);
1442 XSetSelectionOwner(m_display, m_primary, m_window, CurrentTime);
1443 owner= XGetSelectionOwner(m_display, m_primary);
1444 if (txt_select_buffer)
1445 free((void*)txt_select_buffer);
1447 txt_select_buffer = (char*) malloc(strlen(buffer)+1);
1448 strcpy(txt_select_buffer, buffer);
1451 if (owner != m_window)
1452 fprintf(stderr, "failed to own primary\n");