3 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version. 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
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.
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.
22 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23 * All rights reserved.
25 * The Original Code is: all of this file.
27 * Contributor(s): none yet.
29 * ***** END GPL/BL DUAL LICENSE BLOCK *****
36 #include "GHOST_WindowX11.h"
37 #include "GHOST_SystemX11.h"
38 #include "STR_String.h"
39 #include "GHOST_Debug.h"
41 // For standard X11 cursors
42 #include <X11/cursorfont.h>
44 // For obscure full screen mode stuuf
45 // lifted verbatim from blut.
54 #define MWM_HINTS_DECORATIONS (1L << 1)
56 GLXContext GHOST_WindowX11::s_firstContext = NULL;
60 GHOST_SystemX11 *system,
62 const STR_String& title,
67 GHOST_TWindowState state,
68 GHOST_TDrawingContextType type,
69 const bool stereoVisual
71 GHOST_Window(title,left,top,width,height,state,type),
73 m_valid_setup (false),
75 m_invalid_window(false),
81 // Set up the minimum atrributes that we require and see if
82 // X can find us a visual matching those requirements.
84 int attributes[40], i = 0;
87 attributes[i++] = GLX_STEREO;
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;
97 m_visual = glXChooseVisual(m_display, DefaultScreen(m_display), attributes);
99 if (m_visual == NULL) {
100 // barf : no visual meeting these requirements could be found.
104 // Create a bunch of attributes needed to create an X window.
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.
110 XSetWindowAttributes xattributes;
111 memset(&xattributes, 0, sizeof(xattributes));
113 xattributes.colormap= XCreateColormap(
115 RootWindow(m_display, m_visual->screen),
120 xattributes.border_pixel= 0;
122 // Specify which events we are interested in hearing.
124 xattributes.event_mask=
125 ExposureMask | StructureNotifyMask |
126 KeyPressMask | KeyReleaseMask |
127 EnterWindowMask | LeaveWindowMask |
128 ButtonPressMask | ButtonReleaseMask |
129 PointerMotionMask | FocusChangeMask;
131 // create the window!
136 RootWindow(m_display, m_visual->screen),
145 CWBorderPixel|CWColormap|CWEventMask,
149 // Are we in fullscreen mode - then include
150 // some obscure blut code to remove decorations.
152 if (state == GHOST_kWindowStateFullScreen) {
157 atom = XInternAtom(m_display, "_MOTIF_WM_HINTS", False);
160 GHOST_PRINT("Could not intern X atom for _MOTIF_WM_HINTS.\n");
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
167 XChangeProperty(m_display, m_window,
169 PropModeReplace, (unsigned char *) &hints, 4);
173 // Create some hints for the window manager on how
174 // we want this window treated.
176 XSizeHints * xsizehints = XAllocSizeHints();
177 xsizehints->flags = USPosition | USSize;
178 xsizehints->x = left;
180 xsizehints->width = width;
181 xsizehints->height = height;
182 XSetWMNormalHints(m_display, m_window, xsizehints);
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);
197 // now set up the rendering context.
198 if (installDrawingContext(type) == GHOST_kSuccess) {
199 m_valid_setup = true;
200 GHOST_PRINT("Created window\n");
203 XMapWindow(m_display, m_window);
204 GHOST_PRINT("Mapped window\n");
220 return m_valid_setup;
226 const STR_String& title
228 XStoreName(m_display,m_window,title);
239 XFetchName(m_display,m_window,&name);
240 title= name?name:"untitled";
249 // Getting the window bounds under X11 is not
250 // really supported (nor should it be desired).
251 getClientBounds(bounds);
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;
264 XGetGeometry(m_display,m_window,&root_return,&x_return,&y_return,
265 &w_return,&h_return,&border_w_return,&depth_return);
267 clientToScreen(0, 0, screen_x, screen_y);
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;
281 XWindowChanges values;
282 unsigned int value_mask= CWWidth;
283 values.width = width;
284 XConfigureWindow(m_display,m_window,value_mask,&values);
286 return GHOST_kSuccess;
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;
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;
325 // not sure about this one!
330 XTranslateCoordinates(
332 RootWindow(m_display, m_visual->screen),
355 XTranslateCoordinates(
358 RootWindow(m_display, m_visual->screen),
375 return GHOST_kWindowStateNormal;
381 GHOST_TWindowState state
385 if (state == getState()) {
386 return GHOST_kSuccess;
388 return GHOST_kFailure;
396 GHOST_TWindowOrder order
398 if (order == GHOST_kWindowOrderTop) {
399 XWindowAttributes attr;
401 XRaiseWindow(m_display,m_window);
403 XGetWindowAttributes(m_display, m_window, &attr);
405 /* iconized windows give bad match error */
406 if (attr.map_state == IsViewable)
407 XSetInputFocus(m_display, m_window, RevertToPointerRoot,
410 } else if (order == GHOST_kWindowOrderBottom) {
411 XLowerWindow(m_display,m_window);
414 return GHOST_kFailure;
417 return GHOST_kSuccess;
424 if (getDrawingContextType() == GHOST_kDrawingContextTypeOpenGL) {
425 glXSwapBuffers(m_display,m_window);
426 return GHOST_kSuccess;
428 return GHOST_kFailure;
434 activateDrawingContext(
436 if (m_context !=NULL) {
437 glXMakeCurrent(m_display, m_window,m_context);
438 return GHOST_kSuccess;
440 return GHOST_kFailure;
448 // So the idea of this function is to generate an expose event
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.
455 // We implement this by setting a boolean in this class to concatenate
456 // all such calls into a single event for this window.
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.
461 if (m_invalid_window == false) {
462 m_system->addDirtyWindow(this);
463 m_invalid_window = true;
466 return GHOST_kSuccess;
470 * called by the X11 system implementation when expose events
471 * for the window have been pushed onto the GHOST queue
478 m_invalid_window = false;
484 * Closes the window and disposes resources allocated.
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);
495 if (m_empty_cursor) {
496 XFreeCursor(m_display, m_empty_cursor);
498 if (m_custom_cursor) {
499 XFreeCursor(m_display, m_custom_cursor);
503 if (m_context == s_firstContext) {
504 s_firstContext = NULL;
506 glXDestroyContext(m_display, m_context);
508 XDestroyWindow(m_display, m_window);
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.
522 installDrawingContext(
523 GHOST_TDrawingContextType type
525 // only support openGL for now.
526 GHOST_TSuccess success;
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;
534 glXMakeCurrent(m_display, m_window,m_context);
535 success = GHOST_kSuccess;
537 success = GHOST_kFailure;
542 case GHOST_kDrawingContextTypeNone:
543 success = GHOST_kSuccess;
547 success = GHOST_kFailure;
555 * Removes the current drawing context.
556 * @return Indication as to whether removal has succeeded.
560 removeDrawingContext(
562 GHOST_TSuccess success;
564 if (m_context != NULL) {
565 glXDestroyContext(m_display, m_context);
566 success = GHOST_kSuccess;
568 success = GHOST_kFailure;
577 GHOST_TStandardCursor g_cursor
579 unsigned int xcursor_id;
581 #define GtoX(gcurs, xcurs) case gcurs: xcursor_id = xcurs
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;
610 Cursor xcursor = m_standard_cursors[xcursor_id];
613 xcursor = XCreateFontCursor(m_display, xcursor_id);
615 m_standard_cursors[xcursor_id] = xcursor;
628 if (!m_empty_cursor) {
633 /* make a blank cursor */
634 blank = XCreateBitmapFromData (
636 RootWindow(m_display,DefaultScreen(m_display)),
640 m_empty_cursor = XCreatePixmapCursor(m_display, blank, blank, &dummy, &dummy, 0, 0);
641 XFreePixmap(m_display, blank);
644 return m_empty_cursor;
649 setWindowCursorVisibility(
655 xcursor = getStandardCursor( getCursorShape() );
657 xcursor = getEmptyCursor();
660 XDefineCursor(m_display, m_window, xcursor);
663 return GHOST_kSuccess;
668 setWindowCursorShape(
669 GHOST_TStandardCursor shape
671 Cursor xcursor = getStandardCursor( shape );
673 XDefineCursor(m_display, m_window, xcursor);
676 return GHOST_kSuccess;
681 setWindowCustomCursorShape(
682 GHOST_TUns8 bitmap[16][2],
683 GHOST_TUns8 mask[16][2],
688 setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask,
689 16, 16, hotX, hotY, 0, 1);
695 setWindowCustomCursorShape(
705 Pixmap bitmap_pix, mask_pix;
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;
713 if (m_custom_cursor) {
714 XFreeCursor(m_display, m_custom_cursor);
717 bitmap_pix = XCreateBitmapFromData(m_display, m_window, (char*) bitmap, sizex, sizey);
718 mask_pix = XCreateBitmapFromData(m_display, m_window, (char*) mask, sizex, sizey);
720 m_custom_cursor = XCreatePixmapCursor(m_display, bitmap_pix, mask_pix, &fg, &bg, hotX, hotY);
721 XDefineCursor(m_display, m_window, m_custom_cursor);
724 XFreePixmap(m_display, bitmap_pix);
725 XFreePixmap(m_display, mask_pix);
727 return GHOST_kSuccess;
732 void glutCustomCursor(char *data1, char *data2, int size)
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;
744 source= XCreateBitmapFromData(__glutDisplay, xdraw, data2, size, size);
745 mask= XCreateBitmapFromData(__glutDisplay, xdraw, data1, size, size);
747 cursor= XCreatePixmapCursor(__glutDisplay, source, mask, &fg, &bg, 7, 7);
749 XFreePixmap(__glutDisplay, source);
750 XFreePixmap(__glutDisplay, mask);
752 XDefineCursor(__glutDisplay, xdraw, cursor);