Patch #5195 by Anders Gudmundson.
[blender-staging.git] / intern / ghost / intern / GHOST_WindowX11.cpp
1 /**
2  * $Id$
3  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
4  *
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. The Blender
9  * Foundation also sells licenses for use in proprietary software under
10  * the Blender License.  See http://www.blender.org/BL/ for information
11  * about this.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL/BL DUAL LICENSE BLOCK *****
30  */
31
32 #include "GHOST_WindowX11.h"
33 #include "GHOST_SystemX11.h"
34 #include "STR_String.h"
35 #include "GHOST_Debug.h"
36
37 // For standard X11 cursors
38 #include <X11/cursorfont.h>
39
40 // For obscure full screen mode stuuf
41 // lifted verbatim from blut.
42
43 typedef struct {
44         long flags;
45         long functions;
46         long decorations;
47         long input_mode;
48 } MotifWmHints;
49
50 #define MWM_HINTS_DECORATIONS         (1L << 1)
51
52 GLXContext GHOST_WindowX11::s_firstContext = NULL;
53
54 GHOST_WindowX11::
55 GHOST_WindowX11(
56         GHOST_SystemX11 *system,
57         Display * display,
58         const STR_String& title, 
59         GHOST_TInt32 left,
60         GHOST_TInt32 top,
61         GHOST_TUns32 width,     
62         GHOST_TUns32 height,
63         GHOST_TWindowState state,
64         GHOST_TDrawingContextType type,
65         const bool stereoVisual
66 ) :
67         GHOST_Window(title,left,top,width,height,state,type,stereoVisual),
68         m_display(display),
69         m_valid_setup (false),
70         m_system (system),
71         m_invalid_window(false),
72         m_context(NULL),
73         m_empty_cursor(None),
74         m_custom_cursor(None)
75 {
76         
77         // Set up the minimum atrributes that we require and see if
78         // X can find us a visual matching those requirements.
79
80         int attributes[40], i = 0;
81
82         if(m_stereoVisual)
83                 attributes[i++] = GLX_STEREO;
84
85         attributes[i++] = GLX_RGBA;
86         attributes[i++] = GLX_DOUBLEBUFFER;     
87         attributes[i++] = GLX_RED_SIZE;   attributes[i++] = 1;
88         attributes[i++] = GLX_BLUE_SIZE;  attributes[i++] = 1;
89         attributes[i++] = GLX_GREEN_SIZE; attributes[i++] = 1;
90         attributes[i++] = GLX_DEPTH_SIZE; attributes[i++] = 1;
91         attributes[i] = None;
92         
93         m_visual = glXChooseVisual(m_display, DefaultScreen(m_display), attributes);
94
95         if (m_visual == NULL) {
96                 // barf : no visual meeting these requirements could be found.
97                 return;
98         }
99
100         // Create a bunch of attributes needed to create an X window.
101
102
103         // First create a colormap for the window and visual. 
104         // This seems pretty much a legacy feature as we are in rgba mode anyway.
105
106         XSetWindowAttributes xattributes;
107         memset(&xattributes, 0, sizeof(xattributes));
108
109         xattributes.colormap= XCreateColormap(
110                 m_display, 
111                 RootWindow(m_display, m_visual->screen),
112                 m_visual->visual,
113                 AllocNone
114         );
115
116         xattributes.border_pixel= 0;
117
118         // Specify which events we are interested in hearing.   
119
120         xattributes.event_mask= 
121                 ExposureMask | StructureNotifyMask | 
122                 KeyPressMask | KeyReleaseMask |
123                 EnterWindowMask | LeaveWindowMask |
124                 ButtonPressMask | ButtonReleaseMask |
125                 PointerMotionMask | FocusChangeMask;
126
127         // create the window!
128
129         m_window = 
130                 XCreateWindow(
131                         m_display, 
132                         RootWindow(m_display, m_visual->screen), 
133                         left,
134                         top,
135                         width,
136                         height,
137                         0, // no border.
138                         m_visual->depth,
139                         InputOutput, 
140                         m_visual->visual,
141                         CWBorderPixel|CWColormap|CWEventMask, 
142                         &xattributes
143                 );
144
145         // Are we in fullscreen mode - then include
146         // some obscure blut code to remove decorations.
147
148         if (state == GHOST_kWindowStateFullScreen) {
149
150                 MotifWmHints hints;
151                 Atom atom;
152                                         
153                 atom = XInternAtom(m_display, "_MOTIF_WM_HINTS", False);
154                 
155                 if (atom == None) {
156                         GHOST_PRINT("Could not intern X atom for _MOTIF_WM_HINTS.\n");
157                 } else {
158                         hints.flags = MWM_HINTS_DECORATIONS;
159                         hints.decorations = 0;  /* Absolutely no decorations. */
160                         // other hints.decorations make no sense
161                         // you can't select individual decorations
162
163                         XChangeProperty(m_display, m_window,
164                                 atom, atom, 32,
165                                 PropModeReplace, (unsigned char *) &hints, 4);
166                 }               
167         }
168
169         // Create some hints for the window manager on how
170         // we want this window treated. 
171
172         XSizeHints * xsizehints = XAllocSizeHints();
173         xsizehints->flags = USPosition | USSize;
174         xsizehints->x = left;
175         xsizehints->y = top;
176         xsizehints->width = width;
177         xsizehints->height = height;
178         XSetWMNormalHints(m_display, m_window, xsizehints);
179         XFree(xsizehints);
180
181         XClassHint * xclasshint = XAllocClassHint();
182         int len = title.Length() +1 ;
183         char *wmclass = (char *)malloc(sizeof(char) * len);
184         strncpy(wmclass, (const char*)title, sizeof(char) * len);
185         xclasshint->res_name = wmclass;
186         xclasshint->res_class = wmclass;
187         XSetClassHint(m_display, m_window, xclasshint);
188         free(wmclass);
189         XFree(xclasshint);
190
191         setTitle(title);
192
193         initXInputDevices();
194
195         // now set up the rendering context.
196         if (installDrawingContext(type) == GHOST_kSuccess) {
197                 m_valid_setup = true;
198                 GHOST_PRINT("Created window\n");
199         }
200
201         XMapWindow(m_display, m_window);
202         GHOST_PRINT("Mapped window\n");
203
204         XFlush(m_display);
205 }
206
207 /* 
208         Dummy function to get around IO Handler exiting if device invalid
209         Basically it will not crash blender now if you have a X device that 
210         is configured but not plugged in.
211
212 */
213 static int ApplicationErrorHandler(Display *display, XErrorEvent *theEvent) {
214         fprintf(stderr, "Ignoring Xlib error: error code %d request code %d\n",
215                 theEvent->error_code, theEvent->request_code) ;
216
217         /* No exit! - but keep lint happy */
218         return 0 ;
219 }
220
221 void GHOST_WindowX11::initXInputDevices()
222 {
223         static XErrorHandler old_handler = (XErrorHandler) 0 ;
224         XExtensionVersion *version = XGetExtensionVersion(m_display, INAME);
225
226         if(version && (version != (XExtensionVersion*)NoSuchExtension)) {
227                 if(version->present) {
228                         int device_count;
229                         XDeviceInfo* device_info = XListInputDevices(m_display, &device_count);
230                         m_xtablet.StylusDevice = 0;
231                         m_xtablet.EraserDevice = 0;
232                         m_xtablet.CommonData.Active= 0;
233
234                         /* Install our error handler to override Xlib's termination behavior */
235                         old_handler = XSetErrorHandler(ApplicationErrorHandler) ;
236
237                         for(int i=0; i<device_count; ++i) {
238                                 if(!strcasecmp(device_info[i].name, "stylus")) {
239                                         m_xtablet.StylusID= device_info[i].id;
240                                         m_xtablet.StylusDevice = XOpenDevice(m_display, m_xtablet.StylusID);
241
242                                         if (m_xtablet.StylusDevice != NULL) {
243                                                 /* Find how many pressure levels tablet has */
244                                                 XAnyClassPtr ici = device_info[i].inputclassinfo;
245                                                 for(int j=0; j<m_xtablet.StylusDevice->num_classes; ++j) {
246                                                         if(ici->c_class==ValuatorClass) {
247                                                                 XValuatorInfo* xvi = (XValuatorInfo*)ici;
248                                                                 m_xtablet.PressureLevels = xvi->axes[2].max_value;
249                                                         
250                                                                 /* this is assuming that the tablet has the same tilt resolution in both
251                                                                  * positive and negative directions. It would be rather weird if it didn't.. */
252                                                                 m_xtablet.XtiltLevels = xvi->axes[3].max_value;
253                                                                 m_xtablet.YtiltLevels = xvi->axes[4].max_value;
254                                                                 break;
255                                                         }
256                                                 
257                                                         ici = (XAnyClassPtr)(((char *)ici) + ici->length);
258                                                 }
259                                         } else {
260                                                 m_xtablet.StylusID= 0;
261                                         }
262                                 }
263                                 if(!strcasecmp(device_info[i].name, "eraser")) {
264                                         m_xtablet.EraserID= device_info[i].id;
265                                         m_xtablet.EraserDevice = XOpenDevice(m_display, m_xtablet.EraserID);
266                                         if (m_xtablet.EraserDevice == NULL) m_xtablet.EraserID= 0;
267                                 }
268                         }
269
270                         /* Restore handler */
271                         (void) XSetErrorHandler(old_handler) ;
272
273                         XFreeDeviceList(device_info);
274
275
276                         XEventClass xevents[10], ev;
277                         int dcount = 0;
278
279                         if(m_xtablet.StylusDevice) {
280                                 DeviceMotionNotify(m_xtablet.StylusDevice, m_xtablet.MotionEvent, ev);
281                                 if(ev) xevents[dcount++] = ev;
282                                 ProximityIn(m_xtablet.StylusDevice, m_xtablet.ProxInEvent, ev);
283                                 if(ev) xevents[dcount++] = ev;
284                                 ProximityOut(m_xtablet.StylusDevice, m_xtablet.ProxOutEvent, ev);
285                                 if(ev) xevents[dcount++] = ev;
286                         }
287                         if(m_xtablet.EraserDevice) {
288                                 DeviceMotionNotify(m_xtablet.EraserDevice, m_xtablet.MotionEvent, ev);
289                                 if(ev) xevents[dcount++] = ev;
290                                 ProximityIn(m_xtablet.EraserDevice, m_xtablet.ProxInEvent, ev);
291                                 if(ev) xevents[dcount++] = ev;
292                                 ProximityOut(m_xtablet.EraserDevice, m_xtablet.ProxOutEvent, ev);
293                                 if(ev) xevents[dcount++] = ev;
294                         }
295
296                         XSelectExtensionEvent(m_display, m_window, xevents, dcount);
297                 }
298                 XFree(version);
299         }
300 }       
301
302
303         Window 
304 GHOST_WindowX11::
305 getXWindow(
306 ){
307         return m_window;
308 }       
309
310         bool 
311 GHOST_WindowX11::
312 getValid(
313 ) const {
314         return m_valid_setup;
315 }
316
317         void 
318 GHOST_WindowX11::
319 setTitle(
320         const STR_String& title
321 ){
322         XStoreName(m_display,m_window,title);
323         XFlush(m_display);
324 }
325
326         void 
327 GHOST_WindowX11::
328 getTitle(
329         STR_String& title
330 ) const {
331         char *name = NULL;
332         
333         XFetchName(m_display,m_window,&name);
334         title= name?name:"untitled";
335         XFree(name);
336 }
337         
338         void 
339 GHOST_WindowX11::
340 getWindowBounds(
341         GHOST_Rect& bounds
342 ) const {
343                 // Getting the window bounds under X11 is not
344                 // really supported (nor should it be desired).
345         getClientBounds(bounds);
346 }
347
348         void 
349 GHOST_WindowX11::
350 getClientBounds(
351         GHOST_Rect& bounds
352 ) const {
353         Window root_return;
354         int x_return,y_return;
355         unsigned int w_return,h_return,border_w_return,depth_return;
356         GHOST_TInt32 screen_x, screen_y;
357         
358         XGetGeometry(m_display,m_window,&root_return,&x_return,&y_return,
359                 &w_return,&h_return,&border_w_return,&depth_return);
360
361         clientToScreen(0, 0, screen_x, screen_y);
362         
363         bounds.m_l = screen_x;
364         bounds.m_r = bounds.m_l + w_return;
365         bounds.m_t = screen_y;
366         bounds.m_b = bounds.m_t + h_return;
367
368 }
369
370         GHOST_TSuccess 
371 GHOST_WindowX11::
372 setClientWidth(
373         GHOST_TUns32 width
374 ){      
375         XWindowChanges values;
376         unsigned int value_mask= CWWidth;               
377         values.width = width;
378         XConfigureWindow(m_display,m_window,value_mask,&values);
379
380         return GHOST_kSuccess;
381 }
382
383         GHOST_TSuccess 
384 GHOST_WindowX11::
385 setClientHeight(
386         GHOST_TUns32 height
387 ){
388         XWindowChanges values;
389         unsigned int value_mask= CWHeight;              
390         values.height = height;
391         XConfigureWindow(m_display,m_window,value_mask,&values);
392         return GHOST_kSuccess;
393
394 }
395
396         GHOST_TSuccess 
397 GHOST_WindowX11::
398 setClientSize(
399         GHOST_TUns32 width,
400         GHOST_TUns32 height
401 ){
402         XWindowChanges values;
403         unsigned int value_mask= CWWidth | CWHeight;            
404         values.width = width;
405         values.height = height;
406         XConfigureWindow(m_display,m_window,value_mask,&values);
407         return GHOST_kSuccess;
408
409 }       
410
411         void 
412 GHOST_WindowX11::
413 screenToClient(
414         GHOST_TInt32 inX,
415         GHOST_TInt32 inY,
416         GHOST_TInt32& outX,
417         GHOST_TInt32& outY
418 ) const {
419         // not sure about this one!
420
421         int ax,ay;
422         Window temp;
423
424         XTranslateCoordinates(
425                         m_display,
426                         RootWindow(m_display, m_visual->screen),
427                         m_window,
428                         inX,
429                         inY,
430                         &ax,
431                         &ay,
432                         &temp
433                 );
434         outX = ax;
435         outY = ay;
436 }
437                  
438         void 
439 GHOST_WindowX11::
440 clientToScreen(
441         GHOST_TInt32 inX,
442         GHOST_TInt32 inY,
443         GHOST_TInt32& outX,
444         GHOST_TInt32& outY
445 ) const {
446         int ax,ay;
447         Window temp;
448
449         XTranslateCoordinates(
450                         m_display,
451                         m_window,
452                         RootWindow(m_display, m_visual->screen),
453                         inX,
454                         inY,
455                         &ax,
456                         &ay,
457                         &temp
458                 );
459         outX = ax;
460         outY = ay;
461 }
462
463
464         GHOST_TWindowState 
465 GHOST_WindowX11::
466 getState(
467 ) const {
468         //FIXME 
469         return GHOST_kWindowStateNormal;
470 }
471
472         GHOST_TSuccess 
473 GHOST_WindowX11::
474 setState(
475         GHOST_TWindowState state
476 ){
477         //TODO
478
479         if (state == getState()) {
480                 return GHOST_kSuccess;
481         } else {
482                 return GHOST_kFailure;
483         }
484
485 }
486
487 #include <iostream>
488 using namespace std;
489
490         GHOST_TSuccess 
491 GHOST_WindowX11::
492 setOrder(
493         GHOST_TWindowOrder order
494 ){
495         if (order == GHOST_kWindowOrderTop) {
496                 XWindowAttributes attr;   
497                 Atom atom;
498
499                 /* We use both XRaiseWindow and _NET_ACTIVE_WINDOW, since some
500                    window managers ignore the former (e.g. kwin from kde) and others
501                    don't implement the latter (e.g. fluxbox pre 0.9.9) */
502
503                 XRaiseWindow(m_display, m_window);
504
505                 atom = XInternAtom(m_display, "_NET_ACTIVE_WINDOW", True);
506
507                 if (atom != None) {
508                         Window root;
509                         XEvent xev;
510                         long eventmask;
511
512                         xev.xclient.type = ClientMessage;
513                         xev.xclient.serial = 0;
514                         xev.xclient.send_event = True;
515                         xev.xclient.window = m_window;
516                         xev.xclient.message_type = atom;
517
518                         xev.xclient.format = 32;
519                         xev.xclient.data.l[0] = 0;
520                         xev.xclient.data.l[1] = 0;
521                         xev.xclient.data.l[2] = 0;
522                         xev.xclient.data.l[3] = 0;
523                         xev.xclient.data.l[4] = 0;
524
525                         root = RootWindow(m_display, m_visual->screen),
526                         eventmask = SubstructureRedirectMask | SubstructureNotifyMask;
527
528                         XSendEvent(m_display, root, False, eventmask, &xev);
529                 }
530
531                 XGetWindowAttributes(m_display, m_window, &attr);
532
533                 /* iconized windows give bad match error */
534                 if (attr.map_state == IsViewable)
535                         XSetInputFocus(m_display, m_window, RevertToPointerRoot,
536                                                  CurrentTime);
537                 XFlush(m_display);
538         } else if (order == GHOST_kWindowOrderBottom) {
539                 XLowerWindow(m_display,m_window);
540                 XFlush(m_display);
541         } else {
542                 return GHOST_kFailure;
543         }
544         
545         return GHOST_kSuccess;
546 }
547
548         GHOST_TSuccess 
549 GHOST_WindowX11::
550 swapBuffers(
551 ){
552         if (getDrawingContextType() == GHOST_kDrawingContextTypeOpenGL) {
553                 glXSwapBuffers(m_display,m_window);
554                 return GHOST_kSuccess;
555         } else {
556                 return GHOST_kFailure;
557         }
558 }
559
560         GHOST_TSuccess 
561 GHOST_WindowX11::
562 activateDrawingContext(
563 ){
564         if (m_context !=NULL) {
565                 glXMakeCurrent(m_display, m_window,m_context);                                          
566                 return GHOST_kSuccess;
567         } 
568         return GHOST_kFailure;
569 }
570
571         GHOST_TSuccess 
572 GHOST_WindowX11::
573 invalidate(
574 ){
575         
576         // So the idea of this function is to generate an expose event
577         // for the window.
578         // Unfortunately X does not handle expose events for you and 
579         // it is the client's job to refresh the dirty part of the window.
580         // We need to queue up invalidate calls and generate GHOST events 
581         // for them in the system.
582
583         // We implement this by setting a boolean in this class to concatenate 
584         // all such calls into a single event for this window.
585
586         // At the same time we queue the dirty windows in the system class
587         // and generate events for them at the next processEvents call.
588
589         if (m_invalid_window == false) {
590                 m_system->addDirtyWindow(this);
591                 m_invalid_window = true;
592         } 
593  
594         return GHOST_kSuccess;
595 }
596
597 /**
598  * called by the X11 system implementation when expose events
599  * for the window have been pushed onto the GHOST queue
600  */
601  
602         void
603 GHOST_WindowX11::
604 validate(
605 ){
606         m_invalid_window = false;
607 }       
608  
609  
610 /**
611  * Destructor.
612  * Closes the window and disposes resources allocated.
613  */
614
615 GHOST_WindowX11::
616 ~GHOST_WindowX11(
617 ){
618         std::map<unsigned int, Cursor>::iterator it = m_standard_cursors.begin();
619         for (; it != m_standard_cursors.end(); it++) {
620                 XFreeCursor(m_display, it->second);
621         }
622
623         if (m_empty_cursor) {
624                 XFreeCursor(m_display, m_empty_cursor);
625         }
626         if (m_custom_cursor) {
627                 XFreeCursor(m_display, m_custom_cursor);
628         }
629         
630         if (m_context) {
631                 if (m_context == s_firstContext) {
632                         s_firstContext = NULL;
633                 }
634                 glXDestroyContext(m_display, m_context);
635         }
636         XDestroyWindow(m_display, m_window);
637         XFree(m_visual);
638 }
639
640
641
642
643 /**
644  * Tries to install a rendering context in this window.
645  * @param type  The type of rendering context installed.
646  * @return Indication as to whether installation has succeeded.
647  */
648         GHOST_TSuccess 
649 GHOST_WindowX11::
650 installDrawingContext(
651         GHOST_TDrawingContextType type
652 ){
653         // only support openGL for now.
654         GHOST_TSuccess success;
655         switch (type) {
656         case GHOST_kDrawingContextTypeOpenGL:
657                 m_context = glXCreateContext(m_display, m_visual, s_firstContext, True);
658                 if (m_context !=NULL) {
659                         if (!s_firstContext) {
660                                 s_firstContext = m_context;
661                         }
662                         glXMakeCurrent(m_display, m_window,m_context);                                          
663                         success = GHOST_kSuccess;
664                 } else {
665                         success = GHOST_kFailure;
666                 }
667
668                 break;
669
670         case GHOST_kDrawingContextTypeNone:
671                 success = GHOST_kSuccess;
672                 break;
673
674         default:
675                 success = GHOST_kFailure;
676         }
677         return success;
678 }
679
680
681
682 /**
683  * Removes the current drawing context.
684  * @return Indication as to whether removal has succeeded.
685  */
686         GHOST_TSuccess 
687 GHOST_WindowX11::
688 removeDrawingContext(
689 ){
690         GHOST_TSuccess success;
691
692         if (m_context != NULL) {
693                 glXDestroyContext(m_display, m_context);
694                 success = GHOST_kSuccess;
695         } else {
696                 success = GHOST_kFailure;
697         }
698         return success; 
699 }
700
701
702         Cursor
703 GHOST_WindowX11::
704 getStandardCursor(
705         GHOST_TStandardCursor g_cursor
706 ){
707         unsigned int xcursor_id;
708
709 #define GtoX(gcurs, xcurs)      case gcurs: xcursor_id = xcurs
710         switch (g_cursor) {
711         GtoX(GHOST_kStandardCursorRightArrow, XC_arrow); break;
712         GtoX(GHOST_kStandardCursorLeftArrow, XC_top_left_arrow); break;
713         GtoX(GHOST_kStandardCursorInfo, XC_hand1); break;
714         GtoX(GHOST_kStandardCursorDestroy, XC_pirate); break;
715         GtoX(GHOST_kStandardCursorHelp, XC_question_arrow); break; 
716         GtoX(GHOST_kStandardCursorCycle, XC_exchange); break;
717         GtoX(GHOST_kStandardCursorSpray, XC_spraycan); break;
718         GtoX(GHOST_kStandardCursorWait, XC_watch); break;
719         GtoX(GHOST_kStandardCursorText, XC_xterm); break;
720         GtoX(GHOST_kStandardCursorCrosshair, XC_crosshair); break;
721         GtoX(GHOST_kStandardCursorUpDown, XC_sb_v_double_arrow); break;
722         GtoX(GHOST_kStandardCursorLeftRight, XC_sb_h_double_arrow); break;
723         GtoX(GHOST_kStandardCursorTopSide, XC_top_side); break;
724         GtoX(GHOST_kStandardCursorBottomSide, XC_bottom_side); break;
725         GtoX(GHOST_kStandardCursorLeftSide, XC_left_side); break;
726         GtoX(GHOST_kStandardCursorRightSide, XC_right_side); break;
727         GtoX(GHOST_kStandardCursorTopLeftCorner, XC_top_left_corner); break;
728         GtoX(GHOST_kStandardCursorTopRightCorner, XC_top_right_corner); break;
729         GtoX(GHOST_kStandardCursorBottomRightCorner, XC_bottom_right_corner); break;
730         GtoX(GHOST_kStandardCursorBottomLeftCorner, XC_bottom_left_corner); break;
731         GtoX(GHOST_kStandardCursorPencil, XC_pencil); break;
732         default:
733                 xcursor_id = 0;
734         }
735 #undef GtoX
736
737         if (xcursor_id) {
738                 Cursor xcursor = m_standard_cursors[xcursor_id];
739                 
740                 if (!xcursor) {
741                         xcursor = XCreateFontCursor(m_display, xcursor_id);
742
743                         m_standard_cursors[xcursor_id] = xcursor;
744                 }
745                 
746                 return xcursor;
747         } else {
748                 return None;
749         }
750 }
751
752         Cursor 
753 GHOST_WindowX11::
754 getEmptyCursor(
755 ) {
756         if (!m_empty_cursor) {
757                 Pixmap blank;
758                 XColor dummy;
759                 char data[1] = {0};
760                         
761                 /* make a blank cursor */
762                 blank = XCreateBitmapFromData (
763                         m_display, 
764                         RootWindow(m_display,DefaultScreen(m_display)),
765                         data, 1, 1
766                 );
767
768                 m_empty_cursor = XCreatePixmapCursor(m_display, blank, blank, &dummy, &dummy, 0, 0);
769                 XFreePixmap(m_display, blank);
770         }
771
772         return m_empty_cursor;
773 }
774
775         GHOST_TSuccess
776 GHOST_WindowX11::
777 setWindowCursorVisibility(
778         bool visible
779 ){
780         Cursor xcursor;
781         
782         if (visible) {
783                 xcursor = getStandardCursor( getCursorShape() );
784         } else {
785                 xcursor = getEmptyCursor();
786         }
787
788         XDefineCursor(m_display, m_window, xcursor);
789         XFlush(m_display);
790         
791         return GHOST_kSuccess;
792 }
793
794         GHOST_TSuccess
795 GHOST_WindowX11::
796 setWindowCursorShape(
797         GHOST_TStandardCursor shape
798 ){
799         Cursor xcursor = getStandardCursor( shape );
800         
801         XDefineCursor(m_display, m_window, xcursor);
802         XFlush(m_display);
803
804         return GHOST_kSuccess;
805 }
806
807         GHOST_TSuccess
808 GHOST_WindowX11::
809 setWindowCustomCursorShape(
810         GHOST_TUns8 bitmap[16][2], 
811         GHOST_TUns8 mask[16][2], 
812         int hotX, 
813         int hotY
814 ){
815
816 setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask, 
817                                                                         16, 16, hotX, hotY, 0, 1);
818         return GHOST_kSuccess;
819 }
820
821         GHOST_TSuccess
822 GHOST_WindowX11::
823 setWindowCustomCursorShape(     
824         GHOST_TUns8 *bitmap, 
825         GHOST_TUns8 *mask, 
826         int sizex, 
827         int sizey, 
828         int hotX, 
829         int hotY, 
830         int fg_color, 
831         int bg_color
832 ){
833         Pixmap bitmap_pix, mask_pix;
834         XColor fg, bg;
835         
836         if(XAllocNamedColor(m_display, DefaultColormap(m_display, DefaultScreen(m_display)),
837                 "White", &fg, &fg) == 0) return GHOST_kFailure;
838         if(XAllocNamedColor(m_display, DefaultColormap(m_display, DefaultScreen(m_display)),
839                 "Black", &bg, &bg) == 0) return GHOST_kFailure;
840
841         if (m_custom_cursor) {
842                 XFreeCursor(m_display, m_custom_cursor);
843         }
844
845         bitmap_pix = XCreateBitmapFromData(m_display, m_window, (char*) bitmap, sizex, sizey);
846         mask_pix = XCreateBitmapFromData(m_display, m_window, (char*) mask, sizex, sizey);
847                 
848         m_custom_cursor = XCreatePixmapCursor(m_display, bitmap_pix, mask_pix, &fg, &bg, hotX, hotY);
849         XDefineCursor(m_display, m_window, m_custom_cursor);
850         XFlush(m_display);
851         
852         XFreePixmap(m_display, bitmap_pix);
853         XFreePixmap(m_display, mask_pix);
854
855         return GHOST_kSuccess;
856 }
857
858 /*
859
860 void glutCustomCursor(char *data1, char *data2, int size)
861 {
862         Pixmap source, mask;
863         Cursor cursor;
864         XColor fg, bg;
865         
866         if(XAllocNamedColor(__glutDisplay, DefaultColormap(__glutDisplay, __glutScreen),
867                 "White", &fg, &fg) == 0) return;
868         if(XAllocNamedColor(__glutDisplay, DefaultColormap(__glutDisplay, __glutScreen),
869                 "Red", &bg, &bg) == 0) return;
870
871
872         source= XCreateBitmapFromData(__glutDisplay, xdraw, data2, size, size);
873         mask= XCreateBitmapFromData(__glutDisplay, xdraw, data1, size, size);
874                 
875         cursor= XCreatePixmapCursor(__glutDisplay, source, mask, &fg, &bg, 7, 7);
876                 
877         XFreePixmap(__glutDisplay, source);
878         XFreePixmap(__glutDisplay, mask);
879                 
880         XDefineCursor(__glutDisplay, xdraw, cursor);
881 }
882
883 */