Blender  V2.59
GHOST_SystemX11.cpp
Go to the documentation of this file.
00001 /*
00002  * $Id: GHOST_SystemX11.cpp 39153 2011-08-07 16:44:10Z merwin $
00003  * ***** BEGIN GPL LICENSE BLOCK *****
00004  *
00005  * This program is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License
00007  * as published by the Free Software Foundation; either version 2
00008  * of the License, or (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software Foundation,
00017  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018  *
00019  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00020  * All rights reserved.
00021  *
00022  * The Original Code is: all of this file.
00023  *
00024  * Contributor(s): none yet.
00025  *
00026  * Part of this code has been taken from Qt, under LGPL license
00027  * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
00028  *
00029  * ***** END GPL LICENSE BLOCK *****
00030  */
00031 
00037 #include "GHOST_SystemX11.h"
00038 #include "GHOST_WindowX11.h"
00039 #include "GHOST_WindowManager.h"
00040 #include "GHOST_TimerManager.h"
00041 #include "GHOST_EventCursor.h"
00042 #include "GHOST_EventKey.h"
00043 #include "GHOST_EventButton.h"
00044 #include "GHOST_EventWheel.h"
00045 #include "GHOST_DisplayManagerX11.h"
00046 #ifdef WITH_INPUT_NDOF
00047 #include "GHOST_NDOFManagerX11.h"
00048 #endif
00049 
00050 #include "GHOST_Debug.h"
00051 
00052 #include <X11/Xatom.h>
00053 #include <X11/keysym.h>
00054 #include <X11/XKBlib.h> /* allow detectable autorepeate */
00055 
00056 #ifdef WITH_XF86KEYSYM
00057 #include <X11/XF86keysym.h>
00058 #endif
00059 
00060 #ifdef __sgi
00061 
00062 #if defined(_SGI_EXTRA_PREDEFINES) && !defined(NO_FAST_ATOMS)
00063 #include <X11/SGIFastAtom.h>
00064 #else
00065 #define XSGIFastInternAtom(dpy,string,fast_name,how) XInternAtom(dpy,string,how)
00066 #endif
00067 
00068 #endif
00069 
00070 // For timing
00071 
00072 #include <sys/time.h>
00073 #include <unistd.h>
00074 
00075 #include <iostream>
00076 #include <vector>
00077 #include <stdio.h> // for fprintf only
00078 #include <cstdlib> // for exit
00079 
00080 static GHOST_TKey
00081 convertXKey(KeySym key);
00082 
00083 //these are for copy and select copy
00084 static char *txt_cut_buffer= NULL;
00085 static char *txt_select_buffer= NULL;
00086 
00087 using namespace std;
00088 
00089 GHOST_SystemX11::
00090 GHOST_SystemX11(
00091 ) : 
00092         GHOST_System(),
00093         m_start_time(0)
00094 {
00095         m_display = XOpenDisplay(NULL);
00096         
00097         if (!m_display) {
00098                 std::cerr << "Unable to open a display" << std::endl;
00099                 abort(); //was return before, but this would just mean it will crash later
00100         }
00101         
00102 #ifdef __sgi
00103         m_delete_window_atom 
00104           = XSGIFastInternAtom(m_display,
00105                                "WM_DELETE_WINDOW", 
00106                                SGI_XA_WM_DELETE_WINDOW, False);
00107 #else
00108         m_delete_window_atom 
00109           = XInternAtom(m_display, "WM_DELETE_WINDOW", True);
00110 #endif
00111 
00112         m_wm_protocols= XInternAtom(m_display, "WM_PROTOCOLS", False);
00113         m_wm_take_focus= XInternAtom(m_display, "WM_TAKE_FOCUS", False);
00114         m_wm_state= XInternAtom(m_display, "WM_STATE", False);
00115         m_wm_change_state= XInternAtom(m_display, "WM_CHANGE_STATE", False);
00116         m_net_state= XInternAtom(m_display, "_NET_WM_STATE", False);
00117         m_net_max_horz= XInternAtom(m_display,
00118                                         "_NET_WM_STATE_MAXIMIZED_HORZ", False);
00119         m_net_max_vert= XInternAtom(m_display,
00120                                         "_NET_WM_STATE_MAXIMIZED_VERT", False);
00121         m_net_fullscreen= XInternAtom(m_display,
00122                                         "_NET_WM_STATE_FULLSCREEN", False);
00123         m_motif= XInternAtom(m_display, "_MOTIF_WM_HINTS", False);
00124         m_targets= XInternAtom(m_display, "TARGETS", False);
00125         m_string= XInternAtom(m_display, "STRING", False);
00126         m_compound_text= XInternAtom(m_display, "COMPOUND_TEXT", False);
00127         m_text= XInternAtom(m_display, "TEXT", False);
00128         m_clipboard= XInternAtom(m_display, "CLIPBOARD", False);
00129         m_primary= XInternAtom(m_display, "PRIMARY", False);
00130         m_xclip_out= XInternAtom(m_display, "XCLIP_OUT", False);
00131         m_incr= XInternAtom(m_display, "INCR", False);
00132         m_utf8_string= XInternAtom(m_display, "UTF8_STRING", False);
00133         m_last_warp = 0;
00134 
00135 
00136         // compute the initial time
00137         timeval tv;
00138         if (gettimeofday(&tv,NULL) == -1) {
00139                 GHOST_ASSERT(false,"Could not instantiate timer!");
00140         }
00141         
00142         // Taking care not to overflow the tv.tv_sec*1000
00143         m_start_time = GHOST_TUns64(tv.tv_sec)*1000 + tv.tv_usec/1000;
00144         
00145         
00146         /* use detectable autorepeate, mac and windows also do this */
00147         int use_xkb;
00148         int xkb_opcode, xkb_event, xkb_error;
00149         int xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion;
00150         
00151         use_xkb = XkbQueryExtension(m_display, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
00152         if (use_xkb) {
00153                 XkbSetDetectableAutoRepeat(m_display, true, NULL);
00154         }
00155         
00156 }
00157 
00158 GHOST_SystemX11::
00159 ~GHOST_SystemX11()
00160 {
00161         XCloseDisplay(m_display);
00162 }
00163 
00164 
00165         GHOST_TSuccess 
00166 GHOST_SystemX11::
00167 init(
00168 ){
00169         GHOST_TSuccess success = GHOST_System::init();
00170 
00171         if (success) {
00172 #ifdef WITH_INPUT_NDOF
00173                 m_ndofManager = new GHOST_NDOFManagerX11(*this);
00174 #endif
00175                 m_displayManager = new GHOST_DisplayManagerX11(this);
00176 
00177                 if (m_displayManager) {
00178                         return GHOST_kSuccess;
00179                 }
00180         }
00181 
00182         return GHOST_kFailure;
00183 }
00184 
00185         GHOST_TUns64
00186 GHOST_SystemX11::
00187 getMilliSeconds(
00188 ) const {
00189         timeval tv;
00190         if (gettimeofday(&tv,NULL) == -1) {
00191                 GHOST_ASSERT(false,"Could not compute time!");
00192         }
00193 
00194         // Taking care not to overflow the tv.tv_sec*1000
00195         return  GHOST_TUns64(tv.tv_sec)*1000 + tv.tv_usec/1000 - m_start_time;
00196 }
00197         
00198         GHOST_TUns8 
00199 GHOST_SystemX11::
00200 getNumDisplays(
00201 ) const {
00202         return GHOST_TUns8(1);
00203 }
00204 
00209         void 
00210 GHOST_SystemX11::
00211 getMainDisplayDimensions(
00212         GHOST_TUns32& width,
00213         GHOST_TUns32& height
00214 ) const {       
00215         if (m_display) {
00216                 width  = DisplayWidth(m_display, DefaultScreen(m_display));
00217                 height = DisplayHeight(m_display, DefaultScreen(m_display));
00218         }
00219 }
00220 
00237         GHOST_IWindow* 
00238 GHOST_SystemX11::
00239 createWindow(
00240         const STR_String& title,
00241         GHOST_TInt32 left,
00242         GHOST_TInt32 top,
00243         GHOST_TUns32 width,
00244         GHOST_TUns32 height,
00245         GHOST_TWindowState state,
00246         GHOST_TDrawingContextType type,
00247         bool stereoVisual,
00248         const GHOST_TUns16 numOfAASamples,
00249         const GHOST_TEmbedderWindowID parentWindow
00250 ){
00251         GHOST_WindowX11 * window = 0;
00252         
00253         if (!m_display) return 0;
00254         
00255 
00256         
00257 
00258         window = new GHOST_WindowX11 (
00259                 this,m_display,title, left, top, width, height, state, parentWindow, type, stereoVisual
00260         );
00261 
00262         if (window) {
00263                 // Both are now handle in GHOST_WindowX11.cpp
00264                 // Focus and Delete atoms.
00265 
00266                 if (window->getValid()) {
00267                         // Store the pointer to the window 
00268                         m_windowManager->addWindow(window);
00269                         m_windowManager->setActiveWindow(window);
00270                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
00271                 }
00272                 else {
00273                         delete window;
00274                         window = 0;
00275                 }
00276         }
00277         return window;
00278 }
00279 
00280         GHOST_WindowX11 * 
00281 GHOST_SystemX11::
00282 findGhostWindow(
00283         Window xwind
00284 ) const {
00285         
00286         if (xwind == 0) return NULL;
00287 
00288         // It is not entirely safe to do this as the backptr may point
00289         // to a window that has recently been removed. 
00290         // We should always check the window manager's list of windows 
00291         // and only process events on these windows.
00292 
00293         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
00294 
00295         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
00296         vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
00297         
00298         for (; win_it != win_end; ++win_it) {
00299                 GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
00300                 if (window->getXWindow() == xwind) {
00301                         return window;
00302                 }
00303         }
00304         return NULL;
00305         
00306 }
00307 
00308 static void SleepTillEvent(Display *display, GHOST_TInt64 maxSleep) {
00309         int fd = ConnectionNumber(display);
00310         fd_set fds;
00311         
00312         FD_ZERO(&fds);
00313         FD_SET(fd, &fds);
00314 
00315         if (maxSleep == -1) {
00316             select(fd + 1, &fds, NULL, NULL, NULL);
00317         } else {
00318                 timeval tv;
00319 
00320                 tv.tv_sec = maxSleep/1000;
00321                 tv.tv_usec = (maxSleep - tv.tv_sec*1000)*1000;
00322         
00323             select(fd + 1, &fds, NULL, NULL, &tv);
00324         }
00325 }
00326 
00327 /* This function borrowed from Qt's X11 support
00328  * qclipboard_x11.cpp
00329  *  */
00330 struct init_timestamp_data
00331 {
00332     Time timestamp;
00333 };
00334 
00335 static Bool init_timestamp_scanner(Display*, XEvent *event, XPointer arg)
00336 {
00337         init_timestamp_data *data =
00338         reinterpret_cast<init_timestamp_data*>(arg);
00339     switch(event->type)
00340     {
00341     case ButtonPress:
00342     case ButtonRelease:
00343         data->timestamp = event->xbutton.time;
00344         break;
00345     case MotionNotify:
00346         data->timestamp = event->xmotion.time;
00347         break;
00348     case KeyPress:
00349     case KeyRelease:
00350         data->timestamp = event->xkey.time;
00351         break;
00352     case PropertyNotify:
00353         data->timestamp = event->xproperty.time;
00354         break;
00355     case EnterNotify:
00356     case LeaveNotify:
00357         data->timestamp = event->xcrossing.time;
00358         break;
00359     case SelectionClear:
00360         data->timestamp = event->xselectionclear.time;
00361         break;
00362     default:
00363         break;
00364     }
00365 
00366     return false;
00367 }
00368 
00369 Time
00370 GHOST_SystemX11::
00371 lastEventTime(Time default_time) {
00372     init_timestamp_data data;
00373     data.timestamp = default_time;
00374     XEvent ev;
00375     XCheckIfEvent(m_display, &ev, &init_timestamp_scanner, (XPointer)&data);
00376 
00377     return data.timestamp;
00378 }
00379 
00380         bool 
00381 GHOST_SystemX11::
00382 processEvents(
00383         bool waitForEvent
00384 ){
00385         // Get all the current events -- translate them into 
00386         // ghost events and call base class pushEvent() method.
00387         
00388         bool anyProcessed = false;
00389         
00390         do {
00391                 GHOST_TimerManager* timerMgr = getTimerManager();
00392                 
00393                 if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
00394                         GHOST_TUns64 next = timerMgr->nextFireTime();
00395                         
00396                         if (next==GHOST_kFireTimeNever) {
00397                                 SleepTillEvent(m_display, -1);
00398                         } else {
00399                                 GHOST_TInt64 maxSleep = next - getMilliSeconds();
00400 
00401                                 if(maxSleep >= 0)
00402                                         SleepTillEvent(m_display, next - getMilliSeconds());
00403                         }
00404                 }
00405                 
00406                 if (timerMgr->fireTimers(getMilliSeconds())) {
00407                         anyProcessed = true;
00408                 }
00409                 
00410                 while (XPending(m_display)) {
00411                         XEvent xevent;
00412                         XNextEvent(m_display, &xevent);
00413                         processEvent(&xevent);
00414                         anyProcessed = true;
00415                 }
00416                 
00417                 if (generateWindowExposeEvents()) {
00418                         anyProcessed = true;
00419                 }
00420 
00421 #ifdef WITH_INPUT_NDOF
00422                 if (dynamic_cast<GHOST_NDOFManagerX11*>(m_ndofManager)->processEvents()) {
00423                         anyProcessed = true;
00424                 }
00425 #endif
00426                 
00427         } while (waitForEvent && !anyProcessed);
00428         
00429         return anyProcessed;
00430 }
00431 
00432         void
00433 GHOST_SystemX11::processEvent(XEvent *xe)
00434 {
00435         GHOST_WindowX11 * window = findGhostWindow(xe->xany.window);    
00436         GHOST_Event * g_event = NULL;
00437 
00438         if (!window) {
00439                 return;
00440         }
00441         
00442         switch (xe->type) {
00443                 case Expose:
00444                 {
00445                         XExposeEvent & xee = xe->xexpose;
00446 
00447                         if (xee.count == 0) {
00448                                 // Only generate a single expose event
00449                                 // per read of the event queue.
00450 
00451                                 g_event = new
00452                                 GHOST_Event(
00453                                         getMilliSeconds(),
00454                                         GHOST_kEventWindowUpdate,
00455                                         window
00456                                 );
00457                         }
00458                         break;
00459                 }
00460 
00461                 case MotionNotify:
00462                 {
00463                         XMotionEvent &xme = xe->xmotion;
00464                         
00465                         if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
00466                         {
00467                                 GHOST_TInt32 x_new= xme.x_root;
00468                                 GHOST_TInt32 y_new= xme.y_root;
00469                                 GHOST_TInt32 x_accum, y_accum;
00470                                 GHOST_Rect bounds;
00471 
00472                                 /* fallback to window bounds */
00473                                 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
00474                                         window->getClientBounds(bounds);
00475 
00476                                 /* could also clamp to screen bounds
00477                                  * wrap with a window outside the view will fail atm  */
00478                                 bounds.wrapPoint(x_new, y_new, 8); /* offset of one incase blender is at screen bounds */
00479                                 window->getCursorGrabAccum(x_accum, y_accum);
00480 
00481                                 if(x_new != xme.x_root || y_new != xme.y_root) {
00482                                         if (xme.time > m_last_warp) {
00483                                                 /* when wrapping we don't need to add an event because the
00484                                                  * setCursorPosition call will cause a new event after */
00485                                                 setCursorPosition(x_new, y_new); /* wrap */
00486                                                 window->setCursorGrabAccum(x_accum + (xme.x_root - x_new), y_accum + (xme.y_root - y_new));
00487                                                 m_last_warp = lastEventTime(xme.time);
00488                                         } else {
00489                                                 setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
00490                                         }
00491                                 }
00492                                 else {
00493                                         g_event = new
00494                                         GHOST_EventCursor(
00495                                                 getMilliSeconds(),
00496                                                 GHOST_kEventCursorMove,
00497                                                 window,
00498                                                 xme.x_root + x_accum,
00499                                                 xme.y_root + y_accum
00500                                         );
00501                                 }
00502                         }
00503                         else {
00504                                 g_event = new
00505                                 GHOST_EventCursor(
00506                                         getMilliSeconds(),
00507                                         GHOST_kEventCursorMove,
00508                                         window,
00509                                         xme.x_root,
00510                                         xme.y_root
00511                                 );
00512                         }
00513                         break;
00514                 }
00515 
00516                 case KeyPress:
00517                 case KeyRelease:
00518                 {
00519                         XKeyEvent *xke = &(xe->xkey);
00520                 
00521                         KeySym key_sym = XLookupKeysym(xke,0);
00522                         char ascii;
00523                         
00524                         GHOST_TKey gkey = convertXKey(key_sym);
00525                         GHOST_TEventType type = (xke->type == KeyPress) ? 
00526                                 GHOST_kEventKeyDown : GHOST_kEventKeyUp;
00527                         
00528                         if (!XLookupString(xke, &ascii, 1, NULL, NULL)) {
00529                                 ascii = '\0';
00530                         }
00531                         
00532                         g_event = new
00533                         GHOST_EventKey(
00534                                 getMilliSeconds(),
00535                                 type,
00536                                 window,
00537                                 gkey,
00538                                 ascii
00539                         );
00540                         
00541                 break;
00542                 }
00543 
00544                 case ButtonPress:
00545                 case ButtonRelease:
00546                 {
00547                         XButtonEvent & xbe = xe->xbutton;
00548                         GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
00549                         GHOST_TEventType type = (xbe.type == ButtonPress) ? 
00550                                 GHOST_kEventButtonDown : GHOST_kEventButtonUp;
00551 
00552                         /* process wheel mouse events and break, only pass on press events */
00553                         if(xbe.button == Button4) {
00554                                 if(xbe.type == ButtonPress)
00555                                         g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1);
00556                                 break;
00557                         }
00558                         else if(xbe.button == Button5) {
00559                                 if(xbe.type == ButtonPress)
00560                                         g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1);
00561                                 break;
00562                         }
00563                         
00564                         /* process rest of normal mouse buttons */
00565                         if(xbe.button == Button1)
00566                                 gbmask = GHOST_kButtonMaskLeft;
00567                         else if(xbe.button == Button2)
00568                                 gbmask = GHOST_kButtonMaskMiddle;
00569                         else if(xbe.button == Button3)
00570                                 gbmask = GHOST_kButtonMaskRight;
00571                         /* It seems events 6 and 7 are for horizontal scrolling.
00572                         * you can re-order button mapping like this... (swaps 6,7 with 8,9)
00573                         *   xmodmap -e "pointer = 1 2 3 4 5 8 9 6 7" 
00574                         */
00575                         else if(xbe.button == 8)
00576                                 gbmask = GHOST_kButtonMaskButton4;
00577                         else if(xbe.button == 9)
00578                                 gbmask = GHOST_kButtonMaskButton5;
00579                         else
00580                                 break;
00581 
00582                         g_event = new
00583                         GHOST_EventButton(
00584                                 getMilliSeconds(),
00585                                 type,
00586                                 window,
00587                                 gbmask
00588                         );
00589                         break;
00590                 }
00591                         
00592                         // change of size, border, layer etc.
00593                 case ConfigureNotify:
00594                 {
00595                         /* XConfigureEvent & xce = xe->xconfigure; */
00596 
00597                         g_event = new 
00598                         GHOST_Event(
00599                                 getMilliSeconds(),
00600                                 GHOST_kEventWindowSize,
00601                                 window
00602                         );                      
00603                         break;
00604                 }
00605 
00606                 case FocusIn:
00607                 case FocusOut:
00608                 {
00609                         XFocusChangeEvent &xfe = xe->xfocus;
00610 
00611                         // TODO: make sure this is the correct place for activate/deactivate
00612                         // printf("X: focus %s for window %d\n", xfe.type == FocusIn ? "in" : "out", (int) xfe.window);
00613                 
00614                         // May have to look at the type of event and filter some
00615                         // out.
00616                                                                         
00617                         GHOST_TEventType gtype = (xfe.type == FocusIn) ? 
00618                                 GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate;
00619 
00620                         g_event = new 
00621                         GHOST_Event(    
00622                                 getMilliSeconds(),
00623                                 gtype,
00624                                 window
00625                         );
00626                         break;
00627 
00628                 }
00629                 case ClientMessage:
00630                 {
00631                         XClientMessageEvent & xcme = xe->xclient;
00632 
00633 #ifndef __sgi                   
00634                         if (((Atom)xcme.data.l[0]) == m_delete_window_atom) {
00635                                 g_event = new 
00636                                 GHOST_Event(    
00637                                         getMilliSeconds(),
00638                                         GHOST_kEventWindowClose,
00639                                         window
00640                                 );
00641                         } else 
00642 #endif
00643 
00644                         if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
00645                                 XWindowAttributes attr;
00646                                 Window fwin;
00647                                 int revert_to;
00648 
00649                                 /* as ICCCM say, we need reply this event
00650                                  * with a SetInputFocus, the data[1] have
00651                                  * the valid timestamp (send by the wm).
00652                                  *
00653                                  * Some WM send this event before the
00654                                  * window is really mapped (for example
00655                                  * change from virtual desktop), so we need
00656                                  * to be sure that our windows is mapped
00657                                  * or this call fail and close blender.
00658                                  */
00659                                 if (XGetWindowAttributes(m_display, xcme.window, &attr) == True) {
00660                                         if (XGetInputFocus(m_display, &fwin, &revert_to) == True) {
00661                                                 if (attr.map_state == IsViewable) {
00662                                                         if (fwin != xcme.window)
00663                                                                 XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
00664                                                 }
00665                                         }
00666                                 }
00667                         } else {
00668                                 /* Unknown client message, ignore */
00669                         }
00670                         break;
00671                 }
00672                 
00673                 case DestroyNotify:
00674                         ::exit(-1);     
00675                 // We're not interested in the following things.(yet...)
00676                 case NoExpose : 
00677                 case GraphicsExpose :
00678                         break;
00679                 
00680                 case EnterNotify:
00681                 case LeaveNotify:
00682                 {
00683                         /* XCrossingEvents pointer leave enter window.
00684                            also do cursor move here, MotionNotify only
00685                            happens when motion starts & ends inside window.
00686                            we only do moves when the crossing mode is 'normal'
00687                            (really crossing between windows) since some windowmanagers
00688                            also send grab/ungrab crossings for mousewheel events.
00689                         */
00690                         XCrossingEvent &xce = xe->xcrossing;
00691                         if( xce.mode == NotifyNormal ) {
00692                                 g_event = new 
00693                                 GHOST_EventCursor(
00694                                         getMilliSeconds(),
00695                                         GHOST_kEventCursorMove,
00696                                         window,
00697                                         xce.x_root,
00698                                         xce.y_root
00699                                 );
00700                         }
00701 
00702                         // printf("X: %s window %d\n", xce.type == EnterNotify ? "entering" : "leaving", (int) xce.window);
00703 
00704                         if (xce.type == EnterNotify)
00705                                 m_windowManager->setActiveWindow(window);
00706                         else
00707                                 m_windowManager->setWindowInactive(window);
00708 
00709                         break;
00710                 }
00711                 case MapNotify:
00712                         /*
00713                          * From ICCCM:
00714                          * [ Clients can select for StructureNotify on their
00715                          *   top-level windows to track transition between
00716                          *   Normal and Iconic states. Receipt of a MapNotify
00717                          *   event will indicate a transition to the Normal
00718                          *   state, and receipt of an UnmapNotify event will
00719                          *   indicate a transition to the Iconic state. ]
00720                          */
00721                         if (window->m_post_init == True) {
00722                                 /*
00723                                  * Now we are sure that the window is
00724                                  * mapped, so only need change the state.
00725                                  */
00726                                 window->setState (window->m_post_state);
00727                                 window->m_post_init = False;
00728                         }
00729                         break;
00730                 case UnmapNotify:
00731                         break;
00732                 case MappingNotify:
00733                 case ReparentNotify:
00734                         break;
00735                 case SelectionRequest:
00736                 {
00737                         XEvent nxe;
00738                         Atom target, string, compound_text, c_string;
00739                         XSelectionRequestEvent *xse = &xe->xselectionrequest;
00740                         
00741                         target = XInternAtom(m_display, "TARGETS", False);
00742                         string = XInternAtom(m_display, "STRING", False);
00743                         compound_text = XInternAtom(m_display, "COMPOUND_TEXT", False);
00744                         c_string = XInternAtom(m_display, "C_STRING", False);
00745                         
00746                         /* support obsolete clients */
00747                         if (xse->property == None) {
00748                                 xse->property = xse->target;
00749                         }
00750                         
00751                         nxe.xselection.type = SelectionNotify;
00752                         nxe.xselection.requestor = xse->requestor;
00753                         nxe.xselection.property = xse->property;
00754                         nxe.xselection.display = xse->display;
00755                         nxe.xselection.selection = xse->selection;
00756                         nxe.xselection.target = xse->target;
00757                         nxe.xselection.time = xse->time;
00758                         
00759                         /*Check to see if the requestor is asking for String*/
00760                         if(xse->target == string || xse->target == compound_text || xse->target == c_string) {
00761                                 if (xse->selection == XInternAtom(m_display, "PRIMARY", False)) {
00762                                         XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 8, PropModeReplace, (unsigned char*)txt_select_buffer, strlen(txt_select_buffer));
00763                                 } else if (xse->selection == XInternAtom(m_display, "CLIPBOARD", False)) {
00764                                         XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 8, PropModeReplace, (unsigned char*)txt_cut_buffer, strlen(txt_cut_buffer));
00765                                 }
00766                         } else if (xse->target == target) {
00767                                 Atom alist[4];
00768                                 alist[0] = target;
00769                                 alist[1] = string;
00770                                 alist[2] = compound_text;
00771                                 alist[3] = c_string;
00772                                 XChangeProperty(m_display, xse->requestor, xse->property, xse->target, 32, PropModeReplace, (unsigned char*)alist, 4);
00773                                 XFlush(m_display);
00774                         } else  {
00775                                 //Change property to None because we do not support anything but STRING
00776                                 nxe.xselection.property = None;
00777                         }
00778                         
00779                         //Send the event to the client 0 0 == False, SelectionNotify
00780                         XSendEvent(m_display, xse->requestor, 0, 0, &nxe);
00781                         XFlush(m_display);
00782                         break;
00783                 }
00784                 
00785                 default: {
00786 #ifdef WITH_X11_XINPUT
00787                         if(xe->type == window->GetXTablet().MotionEvent) 
00788                         {
00789                                 XDeviceMotionEvent* data = (XDeviceMotionEvent*)xe;
00790                                 window->GetXTablet().CommonData.Pressure= 
00791                                         data->axis_data[2]/((float)window->GetXTablet().PressureLevels);
00792                         
00793                         /* the (short) cast and the &0xffff is bizarre and unexplained anywhere,
00794                          * but I got garbage data without it. Found it in the xidump.c source --matt */
00795                                 window->GetXTablet().CommonData.Xtilt= 
00796                                         (short)(data->axis_data[3]&0xffff)/((float)window->GetXTablet().XtiltLevels);
00797                                 window->GetXTablet().CommonData.Ytilt= 
00798                                         (short)(data->axis_data[4]&0xffff)/((float)window->GetXTablet().YtiltLevels);
00799                         }
00800                         else if(xe->type == window->GetXTablet().ProxInEvent) 
00801                         {
00802                                 XProximityNotifyEvent* data = (XProximityNotifyEvent*)xe;
00803                                 if(data->deviceid == window->GetXTablet().StylusID)
00804                                         window->GetXTablet().CommonData.Active= GHOST_kTabletModeStylus;
00805                                 else if(data->deviceid == window->GetXTablet().EraserID)
00806                                         window->GetXTablet().CommonData.Active= GHOST_kTabletModeEraser;
00807                         }
00808                         else if(xe->type == window->GetXTablet().ProxOutEvent)
00809                                 window->GetXTablet().CommonData.Active= GHOST_kTabletModeNone;
00810 #endif // WITH_X11_XINPUT
00811                         break;
00812                 }
00813         }
00814 
00815         if (g_event) {
00816                 pushEvent(g_event);
00817         }
00818 }
00819 
00820         GHOST_TSuccess 
00821 GHOST_SystemX11::
00822 getModifierKeys(
00823         GHOST_ModifierKeys& keys
00824 ) const {
00825 
00826         // analyse the masks retuned from XQueryPointer.
00827 
00828         memset((void *)m_keyboard_vector,0,sizeof(m_keyboard_vector));
00829 
00830         XQueryKeymap(m_display,(char *)m_keyboard_vector);
00831 
00832         // now translate key symobols into keycodes and
00833         // test with vector.
00834 
00835         const static KeyCode shift_l = XKeysymToKeycode(m_display,XK_Shift_L);
00836         const static KeyCode shift_r = XKeysymToKeycode(m_display,XK_Shift_R);
00837         const static KeyCode control_l = XKeysymToKeycode(m_display,XK_Control_L);
00838         const static KeyCode control_r = XKeysymToKeycode(m_display,XK_Control_R);
00839         const static KeyCode alt_l = XKeysymToKeycode(m_display,XK_Alt_L);
00840         const static KeyCode alt_r = XKeysymToKeycode(m_display,XK_Alt_R);
00841         const static KeyCode super_l = XKeysymToKeycode(m_display,XK_Super_L);
00842         const static KeyCode super_r = XKeysymToKeycode(m_display,XK_Super_R);
00843 
00844         // shift
00845         keys.set(GHOST_kModifierKeyLeftShift, ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) != 0);
00846         keys.set(GHOST_kModifierKeyRightShift, ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) != 0);
00847         // control
00848         keys.set(GHOST_kModifierKeyLeftControl, ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) != 0);
00849         keys.set(GHOST_kModifierKeyRightControl, ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) != 0);
00850         // alt
00851         keys.set(GHOST_kModifierKeyLeftAlt, ((m_keyboard_vector[alt_l >> 3] >> (alt_l & 7)) & 1) != 0);
00852         keys.set(GHOST_kModifierKeyRightAlt, ((m_keyboard_vector[alt_r >> 3] >> (alt_r & 7)) & 1) != 0);
00853         // super (windows) - only one GHOST-kModifierKeyOS, so mapping to either
00854         keys.set(GHOST_kModifierKeyOS, ( ((m_keyboard_vector[super_l >> 3] >> (super_l & 7)) & 1) ||
00855                                          ((m_keyboard_vector[super_r >> 3] >> (super_r & 7)) & 1) ) != 0);
00856 
00857         return GHOST_kSuccess;
00858 }
00859 
00860         GHOST_TSuccess 
00861 GHOST_SystemX11::
00862 getButtons(
00863         GHOST_Buttons& buttons
00864 ) const {
00865 
00866         Window root_return, child_return;
00867         int rx,ry,wx,wy;
00868         unsigned int mask_return;
00869 
00870         if (XQueryPointer(m_display,
00871                           RootWindow(m_display,DefaultScreen(m_display)),
00872                           &root_return,
00873                           &child_return,
00874                           &rx,&ry,
00875                           &wx,&wy,
00876                           &mask_return) == True)
00877         {
00878                 buttons.set(GHOST_kButtonMaskLeft,   (mask_return & Button1Mask) != 0);
00879                 buttons.set(GHOST_kButtonMaskMiddle, (mask_return & Button2Mask) != 0);
00880                 buttons.set(GHOST_kButtonMaskRight,  (mask_return & Button3Mask) != 0);
00881         }
00882         else {
00883                 return GHOST_kFailure;
00884         }       
00885 
00886         return GHOST_kSuccess;
00887 }
00888 
00889 
00890         GHOST_TSuccess 
00891 GHOST_SystemX11::
00892 getCursorPosition(
00893         GHOST_TInt32& x,
00894         GHOST_TInt32& y
00895 ) const {
00896 
00897         Window root_return, child_return;
00898         int rx,ry,wx,wy;
00899         unsigned int mask_return;
00900 
00901         if (XQueryPointer(
00902                 m_display,
00903                 RootWindow(m_display,DefaultScreen(m_display)),
00904                 &root_return,
00905                 &child_return,
00906                 &rx,&ry,
00907                 &wx,&wy,
00908                 &mask_return
00909         ) == False) {
00910                 return GHOST_kFailure;
00911         } else {
00912                 x = rx;
00913                 y = ry;
00914         }       
00915         return GHOST_kSuccess;
00916 }
00917 
00918 
00919         GHOST_TSuccess 
00920 GHOST_SystemX11::
00921 setCursorPosition(
00922         GHOST_TInt32 x,
00923         GHOST_TInt32 y
00924 ) {
00925 
00926         // This is a brute force move in screen coordinates
00927         // XWarpPointer does relative moves so first determine the
00928         // current pointer position.
00929 
00930         int cx,cy;
00931         if (getCursorPosition(cx,cy) == GHOST_kFailure) {
00932                 return GHOST_kFailure;
00933         }
00934 
00935         int relx = x-cx;
00936         int rely = y-cy;
00937 
00938         XWarpPointer(m_display,None,None,0,0,0,0,relx,rely);
00939         XSync(m_display, 0); /* Sync to process all requests */
00940         
00941         return GHOST_kSuccess;
00942 }
00943 
00944 
00945         void
00946 GHOST_SystemX11::
00947 addDirtyWindow(
00948         GHOST_WindowX11 * bad_wind
00949 ){
00950 
00951         GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)");
00952         
00953         m_dirty_windows.push_back(bad_wind);
00954 }
00955 
00956 
00957         bool
00958 GHOST_SystemX11::
00959 generateWindowExposeEvents(
00960 ){
00961 
00962         vector<GHOST_WindowX11 *>::iterator w_start = m_dirty_windows.begin();
00963         vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end();
00964         bool anyProcessed = false;
00965         
00966         for (;w_start != w_end; ++w_start) {
00967                 GHOST_Event * g_event = new 
00968                         GHOST_Event(
00969                                 getMilliSeconds(),
00970                                 GHOST_kEventWindowUpdate,
00971                                 *w_start
00972                         );                      
00973 
00974                 (*w_start)->validate(); 
00975                 
00976                 if (g_event) {
00977                         pushEvent(g_event);
00978                         anyProcessed = true;
00979                 }
00980         }
00981 
00982         m_dirty_windows.clear();
00983         return anyProcessed;
00984 }
00985 
00986 #define GXMAP(k,x,y) case x: k = y; break; 
00987 
00988 static GHOST_TKey
00989 convertXKey(KeySym key)
00990 {
00991         GHOST_TKey type;
00992 
00993         if ((key >= XK_A) && (key <= XK_Z)) {
00994                 type = GHOST_TKey( key - XK_A + int(GHOST_kKeyA));
00995         } else if ((key >= XK_a) && (key <= XK_z)) {
00996                 type = GHOST_TKey(key - XK_a + int(GHOST_kKeyA));
00997         } else if ((key >= XK_0) && (key <= XK_9)) {
00998                 type = GHOST_TKey(key - XK_0 + int(GHOST_kKey0));
00999         } else if ((key >= XK_F1) && (key <= XK_F24)) {
01000                 type = GHOST_TKey(key - XK_F1 + int(GHOST_kKeyF1));
01001 #if defined(__sun) || defined(__sun__) 
01002                 /* This is a bit of a hack, but it looks like sun
01003                    Used F11 and friends for its special keys Stop,again etc..
01004                    So this little patch enables F11 and F12 to work as expected
01005                    following link has documentation on it: 
01006                    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4734408
01007                    also from /usr/include/X11/Sunkeysym.h 
01008 #define SunXK_F36               0x1005FF10      // Labeled F11
01009 #define SunXK_F37               0x1005FF11      // Labeled F12 
01010 
01011                                 mein@cs.umn.edu
01012                  */
01013                 
01014         } else if (key == 268828432) {
01015                 type = GHOST_kKeyF11;
01016         } else if (key == 268828433) {
01017                 type = GHOST_kKeyF12;
01018 #endif
01019         } else {
01020                 switch(key) {
01021                         GXMAP(type,XK_BackSpace,        GHOST_kKeyBackSpace);
01022                         GXMAP(type,XK_Tab,              GHOST_kKeyTab);
01023                         GXMAP(type,XK_Return,           GHOST_kKeyEnter);
01024                         GXMAP(type,XK_Escape,           GHOST_kKeyEsc);
01025                         GXMAP(type,XK_space,            GHOST_kKeySpace);
01026                         
01027                         GXMAP(type,XK_Linefeed,         GHOST_kKeyLinefeed);
01028                         GXMAP(type,XK_semicolon,        GHOST_kKeySemicolon);
01029                         GXMAP(type,XK_period,           GHOST_kKeyPeriod);
01030                         GXMAP(type,XK_comma,            GHOST_kKeyComma);
01031                         GXMAP(type,XK_quoteright,       GHOST_kKeyQuote);
01032                         GXMAP(type,XK_quoteleft,        GHOST_kKeyAccentGrave);
01033                         GXMAP(type,XK_minus,            GHOST_kKeyMinus);
01034                         GXMAP(type,XK_slash,            GHOST_kKeySlash);
01035                         GXMAP(type,XK_backslash,        GHOST_kKeyBackslash);
01036                         GXMAP(type,XK_equal,            GHOST_kKeyEqual);
01037                         GXMAP(type,XK_bracketleft,      GHOST_kKeyLeftBracket);
01038                         GXMAP(type,XK_bracketright,     GHOST_kKeyRightBracket);
01039                         GXMAP(type,XK_Pause,            GHOST_kKeyPause);
01040                         
01041                         GXMAP(type,XK_Shift_L,          GHOST_kKeyLeftShift);
01042                         GXMAP(type,XK_Shift_R,          GHOST_kKeyRightShift);
01043                         GXMAP(type,XK_Control_L,        GHOST_kKeyLeftControl);
01044                         GXMAP(type,XK_Control_R,        GHOST_kKeyRightControl);
01045                         GXMAP(type,XK_Alt_L,            GHOST_kKeyLeftAlt);
01046                         GXMAP(type,XK_Alt_R,            GHOST_kKeyRightAlt);
01047                         GXMAP(type,XK_Super_L,          GHOST_kKeyOS);
01048                         GXMAP(type,XK_Super_R,          GHOST_kKeyOS);
01049 
01050                         GXMAP(type,XK_Insert,           GHOST_kKeyInsert);
01051                         GXMAP(type,XK_Delete,           GHOST_kKeyDelete);
01052                         GXMAP(type,XK_Home,                     GHOST_kKeyHome);
01053                         GXMAP(type,XK_End,                      GHOST_kKeyEnd);
01054                         GXMAP(type,XK_Page_Up,          GHOST_kKeyUpPage);
01055                         GXMAP(type,XK_Page_Down,        GHOST_kKeyDownPage);
01056 
01057                         GXMAP(type,XK_Left,                     GHOST_kKeyLeftArrow);
01058                         GXMAP(type,XK_Right,            GHOST_kKeyRightArrow);
01059                         GXMAP(type,XK_Up,                       GHOST_kKeyUpArrow);
01060                         GXMAP(type,XK_Down,                     GHOST_kKeyDownArrow);
01061 
01062                         GXMAP(type,XK_Caps_Lock,        GHOST_kKeyCapsLock);
01063                         GXMAP(type,XK_Scroll_Lock,      GHOST_kKeyScrollLock);
01064                         GXMAP(type,XK_Num_Lock,         GHOST_kKeyNumLock);
01065                         
01066                                 /* keypad events */
01067                                 
01068                         GXMAP(type,XK_KP_0,                     GHOST_kKeyNumpad0);
01069                         GXMAP(type,XK_KP_1,                     GHOST_kKeyNumpad1);
01070                         GXMAP(type,XK_KP_2,                     GHOST_kKeyNumpad2);
01071                         GXMAP(type,XK_KP_3,                     GHOST_kKeyNumpad3);
01072                         GXMAP(type,XK_KP_4,                     GHOST_kKeyNumpad4);
01073                         GXMAP(type,XK_KP_5,                     GHOST_kKeyNumpad5);
01074                         GXMAP(type,XK_KP_6,                     GHOST_kKeyNumpad6);
01075                         GXMAP(type,XK_KP_7,                     GHOST_kKeyNumpad7);
01076                         GXMAP(type,XK_KP_8,                     GHOST_kKeyNumpad8);
01077                         GXMAP(type,XK_KP_9,                     GHOST_kKeyNumpad9);
01078                         GXMAP(type,XK_KP_Decimal,       GHOST_kKeyNumpadPeriod);
01079 
01080                         GXMAP(type,XK_KP_Insert,        GHOST_kKeyNumpad0);
01081                         GXMAP(type,XK_KP_End,           GHOST_kKeyNumpad1);
01082                         GXMAP(type,XK_KP_Down,          GHOST_kKeyNumpad2);
01083                         GXMAP(type,XK_KP_Page_Down,     GHOST_kKeyNumpad3);
01084                         GXMAP(type,XK_KP_Left,          GHOST_kKeyNumpad4);
01085                         GXMAP(type,XK_KP_Begin,         GHOST_kKeyNumpad5);
01086                         GXMAP(type,XK_KP_Right,         GHOST_kKeyNumpad6);
01087                         GXMAP(type,XK_KP_Home,          GHOST_kKeyNumpad7);
01088                         GXMAP(type,XK_KP_Up,            GHOST_kKeyNumpad8);
01089                         GXMAP(type,XK_KP_Page_Up,       GHOST_kKeyNumpad9);
01090                         GXMAP(type,XK_KP_Delete,        GHOST_kKeyNumpadPeriod);
01091 
01092                         GXMAP(type,XK_KP_Enter,         GHOST_kKeyNumpadEnter);
01093                         GXMAP(type,XK_KP_Add,           GHOST_kKeyNumpadPlus);
01094                         GXMAP(type,XK_KP_Subtract,      GHOST_kKeyNumpadMinus);
01095                         GXMAP(type,XK_KP_Multiply,      GHOST_kKeyNumpadAsterisk);
01096                         GXMAP(type,XK_KP_Divide,        GHOST_kKeyNumpadSlash);
01097 
01098                         /* Media keys in some keyboards and laptops with XFree86/Xorg */
01099 #ifdef WITH_XF86KEYSYM
01100                         GXMAP(type,XF86XK_AudioPlay,    GHOST_kKeyMediaPlay);
01101                         GXMAP(type,XF86XK_AudioStop,    GHOST_kKeyMediaStop);
01102                         GXMAP(type,XF86XK_AudioPrev,    GHOST_kKeyMediaFirst);
01103                         GXMAP(type,XF86XK_AudioRewind,  GHOST_kKeyMediaFirst);
01104                         GXMAP(type,XF86XK_AudioNext,    GHOST_kKeyMediaLast);
01105 #ifdef XF86XK_AudioForward /* Debian lenny's XF86keysym.h has no XF86XK_AudioForward define */
01106                         GXMAP(type,XF86XK_AudioForward, GHOST_kKeyMediaLast);
01107 #endif
01108 #endif
01109 
01110                                 /* some extra sun cruft (NICE KEYBOARD!) */
01111 #ifdef __sun__
01112                         GXMAP(type,0xffde,                      GHOST_kKeyNumpad1);
01113                         GXMAP(type,0xffe0,                      GHOST_kKeyNumpad3);
01114                         GXMAP(type,0xffdc,                      GHOST_kKeyNumpad5);
01115                         GXMAP(type,0xffd8,                      GHOST_kKeyNumpad7);
01116                         GXMAP(type,0xffda,                      GHOST_kKeyNumpad9);
01117 
01118                         GXMAP(type,0xffd6,                      GHOST_kKeyNumpadSlash);
01119                         GXMAP(type,0xffd7,                      GHOST_kKeyNumpadAsterisk);
01120 #endif
01121 
01122                         default :
01123                                 type = GHOST_kKeyUnknown;
01124                                 break;
01125                 }
01126         }
01127 
01128         return type;
01129 }
01130 
01131 #undef GXMAP
01132 
01133 /* from xclip.c xcout() v0.11 */
01134 
01135 #define XCLIB_XCOUT_NONE                0 /* no context */
01136 #define XCLIB_XCOUT_SENTCONVSEL         1 /* sent a request */
01137 #define XCLIB_XCOUT_INCR                2 /* in an incr loop */
01138 #define XCLIB_XCOUT_FALLBACK            3 /* STRING failed, need fallback to UTF8 */
01139 #define XCLIB_XCOUT_FALLBACK_UTF8       4 /* UTF8 failed, move to compouned */
01140 #define XCLIB_XCOUT_FALLBACK_COMP       5 /* compouned failed, move to text. */
01141 #define XCLIB_XCOUT_FALLBACK_TEXT       6
01142 
01143 // Retrieves the contents of a selections.
01144 void GHOST_SystemX11::getClipboard_xcout(XEvent evt,
01145         Atom sel, Atom target, unsigned char **txt,
01146         unsigned long *len, unsigned int *context) const
01147 {
01148         Atom pty_type;
01149         int pty_format;
01150         unsigned char *buffer;
01151         unsigned long pty_size, pty_items;
01152         unsigned char *ltxt= *txt;
01153 
01154         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
01155         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
01156         GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
01157         Window win = window->getXWindow();
01158 
01159         switch (*context) {
01160                 // There is no context, do an XConvertSelection()
01161                 case XCLIB_XCOUT_NONE:
01162                         // Initialise return length to 0
01163                         if (*len > 0) {
01164                                 free(*txt);
01165                                 *len = 0;
01166                         }
01167 
01168                         // Send a selection request
01169                         XConvertSelection(m_display, sel, target, m_xclip_out, win, CurrentTime);
01170                         *context = XCLIB_XCOUT_SENTCONVSEL;
01171                         return;
01172 
01173                 case XCLIB_XCOUT_SENTCONVSEL:
01174                         if (evt.type != SelectionNotify)
01175                                 return;
01176 
01177                         if (target == m_utf8_string && evt.xselection.property == None) {
01178                                 *context= XCLIB_XCOUT_FALLBACK_UTF8;
01179                                 return;
01180                         }
01181                         else if (target == m_compound_text && evt.xselection.property == None) {
01182                                 *context= XCLIB_XCOUT_FALLBACK_COMP;
01183                                 return;
01184                         }
01185                         else if (target == m_text && evt.xselection.property == None) {
01186                                 *context= XCLIB_XCOUT_FALLBACK_TEXT;
01187                                 return;
01188                         }
01189 
01190                         // find the size and format of the data in property
01191                         XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False,
01192                                 AnyPropertyType, &pty_type, &pty_format,
01193                                 &pty_items, &pty_size, &buffer);
01194                         XFree(buffer);
01195 
01196                         if (pty_type == m_incr) {
01197                                 // start INCR mechanism by deleting property
01198                                 XDeleteProperty(m_display, win, m_xclip_out);
01199                                 XFlush(m_display);
01200                                 *context = XCLIB_XCOUT_INCR;
01201                                 return;
01202                         }
01203 
01204                         // if it's not incr, and not format == 8, then there's
01205                         // nothing in the selection (that xclip understands, anyway)
01206 
01207                         if (pty_format != 8) {
01208                                 *context = XCLIB_XCOUT_NONE;
01209                                 return;
01210                         }
01211 
01212                         // not using INCR mechanism, just read the property
01213                         XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size,
01214                                         False, AnyPropertyType, &pty_type,
01215                                         &pty_format, &pty_items, &pty_size, &buffer);
01216 
01217                         // finished with property, delete it
01218                         XDeleteProperty(m_display, win, m_xclip_out);
01219 
01220                         // copy the buffer to the pointer for returned data
01221                         ltxt = (unsigned char *) malloc(pty_items);
01222                         memcpy(ltxt, buffer, pty_items);
01223 
01224                         // set the length of the returned data
01225                         *len = pty_items;
01226                         *txt = ltxt;
01227 
01228                         // free the buffer
01229                         XFree(buffer);
01230 
01231                         *context = XCLIB_XCOUT_NONE;
01232 
01233                         // complete contents of selection fetched, return 1
01234                         return;
01235 
01236                 case XCLIB_XCOUT_INCR:
01237                         // To use the INCR method, we basically delete the
01238                         // property with the selection in it, wait for an
01239                         // event indicating that the property has been created,
01240                         // then read it, delete it, etc.
01241 
01242                         // make sure that the event is relevant
01243                         if (evt.type != PropertyNotify)
01244                                 return;
01245 
01246                         // skip unless the property has a new value
01247                         if (evt.xproperty.state != PropertyNewValue)
01248                                 return;
01249 
01250                         // check size and format of the property
01251                         XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False,
01252                                 AnyPropertyType, &pty_type, &pty_format,
01253                                 &pty_items, &pty_size, (unsigned char **) &buffer);
01254 
01255                         if (pty_format != 8) {
01256                                 // property does not contain text, delete it
01257                                 // to tell the other X client that we have read 
01258                                 // it and to send the next property
01259                                 XFree(buffer);
01260                                 XDeleteProperty(m_display, win, m_xclip_out);
01261                                 return;
01262                         }
01263 
01264                         if (pty_size == 0) {
01265                                 // no more data, exit from loop
01266                                 XFree(buffer);
01267                                 XDeleteProperty(m_display, win, m_xclip_out);
01268                                 *context = XCLIB_XCOUT_NONE;
01269 
01270                                 // this means that an INCR transfer is now
01271                                 // complete, return 1
01272                                 return;
01273                         }
01274 
01275                         XFree(buffer);
01276 
01277                         // if we have come this far, the propery contains
01278                         // text, we know the size.
01279                         XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size,
01280                                 False, AnyPropertyType, &pty_type, &pty_format,
01281                                 &pty_items, &pty_size, (unsigned char **) &buffer);
01282 
01283                         // allocate memory to accommodate data in *txt
01284                         if (*len == 0) {
01285                                 *len = pty_items;
01286                                 ltxt = (unsigned char *) malloc(*len);
01287                         }
01288                         else {
01289                                 *len += pty_items;
01290                                 ltxt = (unsigned char *) realloc(ltxt, *len);
01291                         }
01292 
01293                         // add data to ltxt
01294                         memcpy(&ltxt[*len - pty_items], buffer, pty_items);
01295 
01296                         *txt = ltxt;
01297                         XFree(buffer);
01298 
01299                         // delete property to get the next item
01300                         XDeleteProperty(m_display, win, m_xclip_out);
01301                         XFlush(m_display);
01302                         return;
01303         }
01304         return;
01305 }
01306 
01307 GHOST_TUns8 *GHOST_SystemX11::getClipboard(bool selection) const
01308 {
01309         Atom sseln;
01310         Atom target= m_string;
01311         Window owner;
01312 
01313         // from xclip.c doOut() v0.11
01314         unsigned char *sel_buf;
01315         unsigned long sel_len= 0;
01316         XEvent evt;
01317         unsigned int context= XCLIB_XCOUT_NONE;
01318 
01319         if (selection == True)
01320                 sseln= m_primary;
01321         else
01322                 sseln= m_clipboard;
01323 
01324         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
01325         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
01326         GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
01327         Window win = window->getXWindow();
01328 
01329         /* check if we are the owner. */
01330         owner= XGetSelectionOwner(m_display, sseln);
01331         if (owner == win) {
01332                 if (sseln == m_clipboard) {
01333                         sel_buf= (unsigned char *)malloc(strlen(txt_cut_buffer)+1);
01334                         strcpy((char *)sel_buf, txt_cut_buffer);
01335                         return((GHOST_TUns8*)sel_buf);
01336                 }
01337                 else {
01338                         sel_buf= (unsigned char *)malloc(strlen(txt_select_buffer)+1);
01339                         strcpy((char *)sel_buf, txt_select_buffer);
01340                         return((GHOST_TUns8*)sel_buf);
01341                 }
01342         }
01343         else if (owner == None)
01344                 return(NULL);
01345 
01346         while (1) {
01347                 /* only get an event if xcout() is doing something */
01348                 if (context != XCLIB_XCOUT_NONE)
01349                         XNextEvent(m_display, &evt);
01350 
01351                 /* fetch the selection, or part of it */
01352                 getClipboard_xcout(evt, sseln, target, &sel_buf, &sel_len, &context);
01353 
01354                 /* fallback is needed. set XA_STRING to target and restart the loop. */
01355                 if (context == XCLIB_XCOUT_FALLBACK) {
01356                         context= XCLIB_XCOUT_NONE;
01357                         target= m_string;
01358                         continue;
01359                 }
01360                 else if (context == XCLIB_XCOUT_FALLBACK_UTF8) {
01361                         /* utf8 fail, move to compouned text. */
01362                         context= XCLIB_XCOUT_NONE;
01363                         target= m_compound_text;
01364                         continue;
01365                 }
01366                 else if (context == XCLIB_XCOUT_FALLBACK_COMP) {
01367                         /* compouned text faile, move to text. */
01368                         context= XCLIB_XCOUT_NONE;
01369                         target= m_text;
01370                         continue;
01371                 }
01372 
01373                 /* only continue if xcout() is doing something */
01374                 if (context == XCLIB_XCOUT_NONE)
01375                         break;
01376         }
01377 
01378         if (sel_len) {
01379                 /* only print the buffer out, and free it, if it's not
01380                  * empty
01381                  */
01382                 unsigned char *tmp_data = (unsigned char*) malloc(sel_len+1);
01383                 memcpy((char*)tmp_data, (char*)sel_buf, sel_len);
01384                 tmp_data[sel_len] = '\0';
01385                 
01386                 if (sseln == m_string)
01387                         XFree(sel_buf);
01388                 else
01389                         free(sel_buf);
01390                 
01391                 return (GHOST_TUns8*)tmp_data;
01392         }
01393         return(NULL);
01394 }
01395 
01396 void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const
01397 {
01398         Window m_window, owner;
01399 
01400         vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();      
01401         vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
01402         GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
01403         m_window = window->getXWindow();
01404 
01405         if (buffer) {
01406                 if (selection == False) {
01407                         XSetSelectionOwner(m_display, m_clipboard, m_window, CurrentTime);
01408                         owner= XGetSelectionOwner(m_display, m_clipboard);
01409                         if (txt_cut_buffer)
01410                                 free((void*)txt_cut_buffer);
01411 
01412                         txt_cut_buffer = (char*) malloc(strlen(buffer)+1);
01413                         strcpy(txt_cut_buffer, buffer);
01414                 } else {
01415                         XSetSelectionOwner(m_display, m_primary, m_window, CurrentTime);
01416                         owner= XGetSelectionOwner(m_display, m_primary);
01417                         if (txt_select_buffer)
01418                                 free((void*)txt_select_buffer);
01419 
01420                         txt_select_buffer = (char*) malloc(strlen(buffer)+1);
01421                         strcpy(txt_select_buffer, buffer);
01422                 }
01423 
01424                 if (owner != m_window)
01425                         fprintf(stderr, "failed to own primary\n");
01426         }
01427 }