Commit of cursor framework. Cursors now defined in source/blender/src/cursors.c and
[blender.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 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35
36 #include "GHOST_WindowX11.h"
37 #include "GHOST_SystemX11.h"
38 #include "STR_String.h"
39 #include "GHOST_Debug.h"
40
41 // For standard X11 cursors
42 #include <X11/cursorfont.h>
43
44 // For obscure full screen mode stuuf
45 // lifted verbatim from blut.
46
47 typedef struct {
48   long flags;
49   long functions;
50   long decorations;
51   long input_mode;
52 } MotifWmHints;
53
54 #define MWM_HINTS_DECORATIONS         (1L << 1)
55
56 GLXContext GHOST_WindowX11::s_firstContext = NULL;
57
58 GHOST_WindowX11::
59 GHOST_WindowX11(
60         GHOST_SystemX11 *system,
61         Display * display,
62         const STR_String& title, 
63         GHOST_TInt32 left,
64         GHOST_TInt32 top,
65         GHOST_TUns32 width,     
66         GHOST_TUns32 height,
67         GHOST_TWindowState state,
68         GHOST_TDrawingContextType type,
69         const bool stereoVisual
70 ) :
71         GHOST_Window(title,left,top,width,height,state,type),
72         m_display(display),
73         m_valid_setup (false),
74         m_system (system),
75         m_invalid_window(false),
76         m_context(NULL),
77         m_empty_cursor(None),
78         m_custom_cursor(None)
79 {
80         
81         // Set up the minimum atrributes that we require and see if
82         // X can find us a visual matching those requirements.
83
84         int attributes[40], i = 0;
85
86         if(m_stereoVisual)
87                 attributes[i++] = GLX_STEREO;
88
89         attributes[i++] = GLX_RGBA;
90         attributes[i++] = GLX_DOUBLEBUFFER;     
91         attributes[i++] = GLX_RED_SIZE;   attributes[i++] = 1;
92         attributes[i++] = GLX_BLUE_SIZE;  attributes[i++] = 1;
93         attributes[i++] = GLX_GREEN_SIZE; attributes[i++] = 1;
94         attributes[i++] = GLX_DEPTH_SIZE; attributes[i++] = 1;
95         attributes[i] = None;
96         
97         m_visual = glXChooseVisual(m_display, DefaultScreen(m_display), attributes);
98
99         if (m_visual == NULL) {
100                 // barf : no visual meeting these requirements could be found.
101                 return;
102         }
103
104         // Create a bunch of attributes needed to create an X window.
105
106
107         // First create a colormap for the window and visual. 
108         // This seems pretty much a legacy feature as we are in rgba mode anyway.
109
110         XSetWindowAttributes xattributes;
111         memset(&xattributes, 0, sizeof(xattributes));
112
113         xattributes.colormap= XCreateColormap(
114                 m_display, 
115                 RootWindow(m_display, m_visual->screen),
116                 m_visual->visual,
117                 AllocNone
118         );
119
120         xattributes.border_pixel= 0;
121
122         // Specify which events we are interested in hearing.   
123
124         xattributes.event_mask= 
125                 ExposureMask | StructureNotifyMask | 
126                 KeyPressMask | KeyReleaseMask |
127                 EnterWindowMask | LeaveWindowMask |
128                 ButtonPressMask | ButtonReleaseMask |
129                 PointerMotionMask | FocusChangeMask;
130
131         // create the window!
132
133         m_window = 
134                 XCreateWindow(
135                         m_display, 
136                         RootWindow(m_display, m_visual->screen), 
137                         left,
138                         top,
139                         width,
140                         height,
141                         0, // no border.
142                         m_visual->depth,
143                         InputOutput, 
144                         m_visual->visual,
145                         CWBorderPixel|CWColormap|CWEventMask, 
146                         &xattributes
147                 );
148
149         // Are we in fullscreen mode - then include
150         // some obscure blut code to remove decorations.
151
152         if (state == GHOST_kWindowStateFullScreen) {
153
154                 MotifWmHints hints;
155                 Atom atom;
156                                         
157                 atom = XInternAtom(m_display, "_MOTIF_WM_HINTS", False);
158                 
159                 if (atom == None) {
160                         GHOST_PRINT("Could not intern X atom for _MOTIF_WM_HINTS.\n");
161                 } else {
162                         hints.flags = MWM_HINTS_DECORATIONS;
163                         hints.decorations = 0;  /* Absolutely no decorations. */
164                         // other hints.decorations make no sense
165                         // you can't select individual decorations
166
167                         XChangeProperty(m_display, m_window,
168                                 atom, atom, 32,
169                                 PropModeReplace, (unsigned char *) &hints, 4);
170                 }               
171         }
172
173         // Create some hints for the window manager on how
174         // we want this window treated. 
175
176         XSizeHints * xsizehints = XAllocSizeHints();
177         xsizehints->flags = USPosition | USSize;
178         xsizehints->x = left;
179         xsizehints->y = top;
180         xsizehints->width = width;
181         xsizehints->height = height;
182         XSetWMNormalHints(m_display, m_window, xsizehints);
183         XFree(xsizehints);
184
185         XClassHint * xclasshint = XAllocClassHint();
186         int len = title.Length() +1 ;
187         char *wmclass = (char *)malloc(sizeof(char) * len);
188         strncpy(wmclass, (const char*)title, sizeof(char) * len);
189         xclasshint->res_name = wmclass;
190         xclasshint->res_class = wmclass;
191         XSetClassHint(m_display, m_window, xclasshint);
192         free(wmclass);
193         XFree(xclasshint);
194
195         setTitle(title);
196         
197         // now set up the rendering context.
198         if (installDrawingContext(type) == GHOST_kSuccess) {
199                 m_valid_setup = true;
200                 GHOST_PRINT("Created window\n");
201         }
202
203         XMapWindow(m_display, m_window);
204         GHOST_PRINT("Mapped window\n");
205
206         XFlush(m_display);
207 }
208
209         Window 
210 GHOST_WindowX11::
211 getXWindow(
212 ){
213         return m_window;
214 }       
215
216         bool 
217 GHOST_WindowX11::
218 getValid(
219 ) const {
220         return m_valid_setup;
221 }
222
223         void 
224 GHOST_WindowX11::
225 setTitle(
226         const STR_String& title
227 ){
228         XStoreName(m_display,m_window,title);
229         XFlush(m_display);
230 }
231
232         void 
233 GHOST_WindowX11::
234 getTitle(
235         STR_String& title
236 ) const {
237         char *name = NULL;
238         
239         XFetchName(m_display,m_window,&name);
240         title= name?name:"untitled";
241         XFree(name);
242 }
243         
244         void 
245 GHOST_WindowX11::
246 getWindowBounds(
247         GHOST_Rect& bounds
248 ) const {
249                 // Getting the window bounds under X11 is not
250                 // really supported (nor should it be desired).
251         getClientBounds(bounds);
252 }
253
254         void 
255 GHOST_WindowX11::
256 getClientBounds(
257         GHOST_Rect& bounds
258 ) const {
259         Window root_return;
260         int x_return,y_return;
261         unsigned int w_return,h_return,border_w_return,depth_return;
262         GHOST_TInt32 screen_x, screen_y;
263         
264         XGetGeometry(m_display,m_window,&root_return,&x_return,&y_return,
265                 &w_return,&h_return,&border_w_return,&depth_return);
266
267         clientToScreen(0, 0, screen_x, screen_y);
268         
269         bounds.m_l = screen_x;
270         bounds.m_r = bounds.m_l + w_return;
271         bounds.m_t = screen_y;
272         bounds.m_b = bounds.m_t + h_return;
273
274 }
275
276         GHOST_TSuccess 
277 GHOST_WindowX11::
278 setClientWidth(
279         GHOST_TUns32 width
280 ){      
281         XWindowChanges values;
282         unsigned int value_mask= CWWidth;               
283         values.width = width;
284         XConfigureWindow(m_display,m_window,value_mask,&values);
285
286         return GHOST_kSuccess;
287 }
288
289         GHOST_TSuccess 
290 GHOST_WindowX11::
291 setClientHeight(
292         GHOST_TUns32 height
293 ){
294         XWindowChanges values;
295         unsigned int value_mask= CWHeight;              
296         values.height = height;
297         XConfigureWindow(m_display,m_window,value_mask,&values);
298         return GHOST_kSuccess;
299
300 }
301
302         GHOST_TSuccess 
303 GHOST_WindowX11::
304 setClientSize(
305         GHOST_TUns32 width,
306         GHOST_TUns32 height
307 ){
308         XWindowChanges values;
309         unsigned int value_mask= CWWidth | CWHeight;            
310         values.width = width;
311         values.height = height;
312         XConfigureWindow(m_display,m_window,value_mask,&values);
313         return GHOST_kSuccess;
314
315 }       
316
317         void 
318 GHOST_WindowX11::
319 screenToClient(
320         GHOST_TInt32 inX,
321         GHOST_TInt32 inY,
322         GHOST_TInt32& outX,
323         GHOST_TInt32& outY
324 ) const {
325         // not sure about this one!
326
327         int ax,ay;
328         Window temp;
329
330         XTranslateCoordinates(
331                         m_display,
332                         RootWindow(m_display, m_visual->screen),
333                         m_window,
334                         inX,
335                         inY,
336                         &ax,
337                         &ay,
338                         &temp
339                 );
340         outX = ax;
341         outY = ay;
342 }
343                  
344         void 
345 GHOST_WindowX11::
346 clientToScreen(
347         GHOST_TInt32 inX,
348         GHOST_TInt32 inY,
349         GHOST_TInt32& outX,
350         GHOST_TInt32& outY
351 ) const {
352         int ax,ay;
353         Window temp;
354
355         XTranslateCoordinates(
356                         m_display,
357                         m_window,
358                         RootWindow(m_display, m_visual->screen),
359                         inX,
360                         inY,
361                         &ax,
362                         &ay,
363                         &temp
364                 );
365         outX = ax;
366         outY = ay;
367 }
368
369
370         GHOST_TWindowState 
371 GHOST_WindowX11::
372 getState(
373 ) const {
374         //FIXME 
375         return GHOST_kWindowStateNormal;
376 }
377
378         GHOST_TSuccess 
379 GHOST_WindowX11::
380 setState(
381         GHOST_TWindowState state
382 ){
383         //TODO
384
385         if (state == getState()) {
386                 return GHOST_kSuccess;
387         } else {
388                 return GHOST_kFailure;
389         }
390
391 }
392
393         GHOST_TSuccess 
394 GHOST_WindowX11::
395 setOrder(
396         GHOST_TWindowOrder order
397 ){
398         if (order == GHOST_kWindowOrderTop) {
399                 XWindowAttributes attr;   
400
401                 XRaiseWindow(m_display,m_window);
402
403                 XGetWindowAttributes(m_display, m_window, &attr);
404
405                 /* iconized windows give bad match error */
406                 if (attr.map_state == IsViewable)
407                   XSetInputFocus(m_display, m_window, RevertToPointerRoot,
408                                                  CurrentTime);
409                 XFlush(m_display);
410         } else if (order == GHOST_kWindowOrderBottom) {
411                 XLowerWindow(m_display,m_window);
412                 XFlush(m_display);
413         } else {
414                 return GHOST_kFailure;
415         }
416         
417         return GHOST_kSuccess;
418 }
419
420         GHOST_TSuccess 
421 GHOST_WindowX11::
422 swapBuffers(
423 ){
424         if (getDrawingContextType() == GHOST_kDrawingContextTypeOpenGL) {
425                 glXSwapBuffers(m_display,m_window);
426                 return GHOST_kSuccess;
427         } else {
428                 return GHOST_kFailure;
429         }
430 }
431
432         GHOST_TSuccess 
433 GHOST_WindowX11::
434 activateDrawingContext(
435 ){
436         if (m_context !=NULL) {
437                 glXMakeCurrent(m_display, m_window,m_context);                                          
438                 return GHOST_kSuccess;
439         } 
440         return GHOST_kFailure;
441 }
442
443         GHOST_TSuccess 
444 GHOST_WindowX11::
445 invalidate(
446 ){
447         
448         // So the idea of this function is to generate an expose event
449         // for the window.
450         // Unfortunately X does not handle expose events for you and 
451         // it is the client's job to refresh the dirty part of the window.
452         // We need to queue up invalidate calls and generate GHOST events 
453         // for them in the system.
454
455         // We implement this by setting a boolean in this class to concatenate 
456         // all such calls into a single event for this window.
457
458         // At the same time we queue the dirty windows in the system class
459         // and generate events for them at the next processEvents call.
460
461         if (m_invalid_window == false) {
462                 m_system->addDirtyWindow(this);
463                 m_invalid_window = true;
464         } 
465  
466         return GHOST_kSuccess;
467 }
468
469 /**
470  * called by the X11 system implementation when expose events
471  * for the window have been pushed onto the GHOST queue
472  */
473  
474         void
475 GHOST_WindowX11::
476 validate(
477 ){
478         m_invalid_window = false;
479 }       
480  
481  
482 /**
483  * Destructor.
484  * Closes the window and disposes resources allocated.
485  */
486
487 GHOST_WindowX11::
488 ~GHOST_WindowX11(
489 ){
490         std::map<unsigned int, Cursor>::iterator it = m_standard_cursors.begin();
491         for (; it != m_standard_cursors.end(); it++) {
492                 XFreeCursor(m_display, it->second);
493         }
494
495         if (m_empty_cursor) {
496                 XFreeCursor(m_display, m_empty_cursor);
497         }
498         if (m_custom_cursor) {
499                 XFreeCursor(m_display, m_custom_cursor);
500         }
501         
502         if (m_context) {
503                 if (m_context == s_firstContext) {
504                         s_firstContext = NULL;
505                 }
506                 glXDestroyContext(m_display, m_context);
507         }
508         XDestroyWindow(m_display, m_window);
509         XFree(m_visual);
510 }
511
512
513
514
515 /**
516  * Tries to install a rendering context in this window.
517  * @param type  The type of rendering context installed.
518  * @return Indication as to whether installation has succeeded.
519  */
520         GHOST_TSuccess 
521 GHOST_WindowX11::
522 installDrawingContext(
523         GHOST_TDrawingContextType type
524 ){
525         // only support openGL for now.
526         GHOST_TSuccess success;
527         switch (type) {
528         case GHOST_kDrawingContextTypeOpenGL:
529                 m_context = glXCreateContext(m_display, m_visual, s_firstContext, True);
530                 if (m_context !=NULL) {
531                         if (!s_firstContext) {
532                                 s_firstContext = m_context;
533                         }
534                         glXMakeCurrent(m_display, m_window,m_context);                                          
535                         success = GHOST_kSuccess;
536                 } else {
537                         success = GHOST_kFailure;
538                 }
539
540                 break;
541
542         case GHOST_kDrawingContextTypeNone:
543                 success = GHOST_kSuccess;
544                 break;
545
546         default:
547                 success = GHOST_kFailure;
548         }
549         return success;
550 }
551
552
553
554 /**
555  * Removes the current drawing context.
556  * @return Indication as to whether removal has succeeded.
557  */
558         GHOST_TSuccess 
559 GHOST_WindowX11::
560 removeDrawingContext(
561 ){
562         GHOST_TSuccess success;
563
564         if (m_context != NULL) {
565                 glXDestroyContext(m_display, m_context);
566                 success = GHOST_kSuccess;
567         } else {
568                 success = GHOST_kFailure;
569         }
570         return success; 
571 }
572
573
574         Cursor
575 GHOST_WindowX11::
576 getStandardCursor(
577         GHOST_TStandardCursor g_cursor
578 ){
579         unsigned int xcursor_id;
580
581 #define GtoX(gcurs, xcurs)      case gcurs: xcursor_id = xcurs
582         switch (g_cursor) {
583         GtoX(GHOST_kStandardCursorRightArrow, XC_arrow); break;
584         GtoX(GHOST_kStandardCursorLeftArrow, XC_top_left_arrow); break;
585         GtoX(GHOST_kStandardCursorInfo, XC_hand1); break;
586         GtoX(GHOST_kStandardCursorDestroy, XC_pirate); break;
587         GtoX(GHOST_kStandardCursorHelp, XC_question_arrow); break; 
588         GtoX(GHOST_kStandardCursorCycle, XC_exchange); break;
589         GtoX(GHOST_kStandardCursorSpray, XC_spraycan); break;
590         GtoX(GHOST_kStandardCursorWait, XC_watch); break;
591         GtoX(GHOST_kStandardCursorText, XC_xterm); break;
592         GtoX(GHOST_kStandardCursorCrosshair, XC_crosshair); break;
593         GtoX(GHOST_kStandardCursorUpDown, XC_sb_v_double_arrow); break;
594         GtoX(GHOST_kStandardCursorLeftRight, XC_sb_h_double_arrow); break;
595         GtoX(GHOST_kStandardCursorTopSide, XC_top_side); break;
596         GtoX(GHOST_kStandardCursorBottomSide, XC_bottom_side); break;
597         GtoX(GHOST_kStandardCursorLeftSide, XC_left_side); break;
598         GtoX(GHOST_kStandardCursorRightSide, XC_right_side); break;
599         GtoX(GHOST_kStandardCursorTopLeftCorner, XC_top_left_corner); break;
600         GtoX(GHOST_kStandardCursorTopRightCorner, XC_top_right_corner); break;
601         GtoX(GHOST_kStandardCursorBottomRightCorner, XC_bottom_right_corner); break;
602         GtoX(GHOST_kStandardCursorBottomLeftCorner, XC_bottom_left_corner); break;
603         GtoX(GHOST_kStandardCursorPencil, XC_pencil); break;
604         default:
605                 xcursor_id = 0;
606         }
607 #undef GtoX
608
609         if (xcursor_id) {
610                 Cursor xcursor = m_standard_cursors[xcursor_id];
611                 
612                 if (!xcursor) {
613                         xcursor = XCreateFontCursor(m_display, xcursor_id);
614
615                         m_standard_cursors[xcursor_id] = xcursor;
616                 }
617                 
618                 return xcursor;
619         } else {
620                 return None;
621         }
622 }
623
624         Cursor 
625 GHOST_WindowX11::
626 getEmptyCursor(
627 ) {
628         if (!m_empty_cursor) {
629                 Pixmap blank;
630                 XColor dummy;
631                 char data[1] = {0};
632                         
633                 /* make a blank cursor */
634                 blank = XCreateBitmapFromData (
635                         m_display, 
636                         RootWindow(m_display,DefaultScreen(m_display)),
637                         data, 1, 1
638                 );
639
640                 m_empty_cursor = XCreatePixmapCursor(m_display, blank, blank, &dummy, &dummy, 0, 0);
641                 XFreePixmap(m_display, blank);
642         }
643     
644         return m_empty_cursor;
645 }
646
647         GHOST_TSuccess
648 GHOST_WindowX11::
649 setWindowCursorVisibility(
650         bool visible
651 ){
652         Cursor xcursor;
653         
654         if (visible) {
655                 xcursor = getStandardCursor( getCursorShape() );
656         } else {
657                 xcursor = getEmptyCursor();
658         }
659
660         XDefineCursor(m_display, m_window, xcursor);
661         XFlush(m_display);
662         
663         return GHOST_kSuccess;
664 }
665
666         GHOST_TSuccess
667 GHOST_WindowX11::
668 setWindowCursorShape(
669         GHOST_TStandardCursor shape
670 ){
671         Cursor xcursor = getStandardCursor( shape );
672         
673         XDefineCursor(m_display, m_window, xcursor);
674         XFlush(m_display);
675
676         return GHOST_kSuccess;
677 }
678
679         GHOST_TSuccess
680 GHOST_WindowX11::
681 setWindowCustomCursorShape(
682         GHOST_TUns8 bitmap[16][2], 
683         GHOST_TUns8 mask[16][2], 
684         int hotX, 
685         int hotY
686 ){
687
688 setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask, 
689                                                                         16, 16, hotX, hotY, 0, 1);
690
691 }
692
693         GHOST_TSuccess
694 GHOST_WindowX11::
695 setWindowCustomCursorShape(     
696         GHOST_TUns8 *bitmap, 
697         GHOST_TUns8 *mask, 
698         int sizex, 
699         int sizey, 
700         int hotX, 
701         int hotY, 
702         int fg_color, 
703         int bg_color
704 ){
705         Pixmap bitmap_pix, mask_pix;
706         XColor fg, bg;
707         
708         if(XAllocNamedColor(m_display, DefaultColormap(m_display, DefaultScreen(m_display)),
709                 "White", &fg, &fg) == 0) return GHOST_kFailure;
710         if(XAllocNamedColor(m_display, DefaultColormap(m_display, DefaultScreen(m_display)),
711                 "Black", &bg, &bg) == 0) return GHOST_kFailure;
712
713         if (m_custom_cursor) {
714                 XFreeCursor(m_display, m_custom_cursor);
715         }
716
717         bitmap_pix = XCreateBitmapFromData(m_display,  m_window, (char*) bitmap, sizex, sizey);
718         mask_pix = XCreateBitmapFromData(m_display, m_window, (char*) mask, sizex, sizey);
719                 
720         m_custom_cursor = XCreatePixmapCursor(m_display, bitmap_pix, mask_pix, &fg, &bg, hotX, hotY);
721         XDefineCursor(m_display, m_window, m_custom_cursor);
722         XFlush(m_display);
723         
724         XFreePixmap(m_display, bitmap_pix);
725         XFreePixmap(m_display, mask_pix);
726
727         return GHOST_kSuccess;
728 }
729
730 /*
731
732 void glutCustomCursor(char *data1, char *data2, int size)
733 {
734         Pixmap source, mask;
735         Cursor cursor;
736         XColor fg, bg;
737         
738         if(XAllocNamedColor(__glutDisplay, DefaultColormap(__glutDisplay, __glutScreen),
739                 "White", &fg, &fg) == 0) return;
740         if(XAllocNamedColor(__glutDisplay, DefaultColormap(__glutDisplay, __glutScreen),
741                 "Red", &bg, &bg) == 0) return;
742
743
744         source= XCreateBitmapFromData(__glutDisplay,  xdraw, data2, size, size);
745         mask= XCreateBitmapFromData(__glutDisplay, xdraw, data1, size, size);
746                 
747         cursor= XCreatePixmapCursor(__glutDisplay, source, mask, &fg, &bg, 7, 7);
748                 
749         XFreePixmap(__glutDisplay, source);
750         XFreePixmap(__glutDisplay, mask);
751                 
752         XDefineCursor(__glutDisplay, xdraw, cursor);
753 }
754
755 */