diff options
Diffstat (limited to 'intern/ghost/intern/GHOST_SystemX11.cpp')
-rwxr-xr-x | intern/ghost/intern/GHOST_SystemX11.cpp | 803 |
1 files changed, 803 insertions, 0 deletions
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp new file mode 100755 index 00000000000..77c13d66de2 --- /dev/null +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -0,0 +1,803 @@ +/** + * $Id$ + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +/** + * $Id$ + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + + +#include "GHOST_SystemX11.h" +#include "GHOST_WindowX11.h" +#include "GHOST_WindowManager.h" +#include "GHOST_TimerManager.h" +#include "GHOST_EventCursor.h" +#include "GHOST_EventKey.h" +#include "GHOST_EventButton.h" +#include "GHOST_DisplayManagerX11.h" + +#include "GHOST_Debug.h" + +#include <X11/Xatom.h> +#include <X11/keysym.h> + +// For timing + +#include <sys/time.h> +#include <unistd.h> + +#include <vector> + +using namespace std; + +GHOST_SystemX11:: +GHOST_SystemX11( +) : + GHOST_System(), + m_start_time(0) +{ + m_display = XOpenDisplay(NULL); + + if (!m_display) return; + + m_delete_window_atom = XInternAtom(m_display, "WM_DELETE_WINDOW", True); + + // compute the initial time + timeval tv; + if (gettimeofday(&tv,NULL) == -1) { + GHOST_ASSERT(false,"Could not instantiate timer!"); + } + + m_start_time = GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000); +} + + GHOST_TSuccess +GHOST_SystemX11:: +init( +){ + GHOST_TSuccess success = GHOST_System::init(); + + if (success) { + m_keyboard_vector = new char[32]; + + m_displayManager = new GHOST_DisplayManagerX11(this); + + if (m_keyboard_vector && m_displayManager) { + return GHOST_kSuccess; + } + } + + return GHOST_kFailure; +} + + + + GHOST_TUns64 +GHOST_SystemX11:: +getMilliSeconds( +) const { + timeval tv; + if (gettimeofday(&tv,NULL) == -1) { + GHOST_ASSERT(false,"Could not compute time!"); + } + + return GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000) - m_start_time; +} + + GHOST_TUns8 +GHOST_SystemX11:: +getNumDisplays( +) const { + return GHOST_TUns8(1); +} + + /** + * Returns the dimensions of the main display on this system. + * @return The dimension of the main display. + */ + void +GHOST_SystemX11:: +getMainDisplayDimensions( + GHOST_TUns32& width, + GHOST_TUns32& height +) const { + if (m_display) { + width = DisplayWidth(m_display, DefaultScreen(m_display)); + height = DisplayHeight(m_display, DefaultScreen(m_display)); + } +} + + /** + * Create a new window. + * The new window is added to the list of windows managed. + * Never explicitly delete the window, use disposeWindow() instead. + * @param title The name of the window (displayed in the title bar of the window if the OS supports it). + * @param left The coordinate of the left edge of the window. + * @param top The coordinate of the top edge of the window. + * @param width The width the window. + * @param height The height the window. + * @param state The state of the window when opened. + * @param type The type of drawing context installed in this window. + * @return The new window (or 0 if creation failed). + */ + GHOST_IWindow* +GHOST_SystemX11:: +createWindow( + const STR_String& title, + GHOST_TInt32 left, + GHOST_TInt32 top, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + GHOST_TDrawingContextType type, + bool stereoVisual +){ + GHOST_WindowX11 * window = 0; + + if (!m_display) return 0; + + window = new GHOST_WindowX11 ( + this,m_display,title, left, top, width, height, state, type + ); + + if (window) { + + // Install a new protocol for this window - so we can overide + // the default window closure mechanism. + + XSetWMProtocols(m_display, window->getXWindow(), &m_delete_window_atom, 1); + + if (window->getValid()) { + // Store the pointer to the window + m_windowManager->addWindow(window); + + pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) ); + } + else { + delete window; + window = 0; + } + } + return window; + +} + + GHOST_WindowX11 * +GHOST_SystemX11:: +findGhostWindow( + Window xwind +) const { + + if (xwind == 0) return NULL; + + // It is not entirely safe to do this as the backptr may point + // to a window that has recently been removed. + // We should always check the window manager's list of windows + // and only process events on these windows. + + vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows(); + + vector<GHOST_IWindow *>::iterator win_it = win_vec.begin(); + vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end(); + + for (; win_it != win_end; ++win_it) { + GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it); + if (window->getXWindow() == xwind) { + return window; + } + } + return NULL; + +} + +static void SleepTillEvent(Display *display, GHOST_TUns64 maxSleep) { + int fd = ConnectionNumber(display); + fd_set fds; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if (maxSleep == -1) { + select(fd + 1, &fds, NULL, NULL, NULL); + } else { + timeval tv; + + tv.tv_sec = maxSleep/1000; + tv.tv_usec = (maxSleep - tv.tv_sec*1000)*1000; + + select(fd + 1, &fds, NULL, NULL, &tv); + } +} + + bool +GHOST_SystemX11:: +processEvents( + bool waitForEvent +){ + // Get all the current events -- translate them into + // ghost events and call base class pushEvent() method. + + bool anyProcessed = false; + + do { + GHOST_TimerManager* timerMgr = getTimerManager(); + + if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) { + GHOST_TUns64 next = timerMgr->nextFireTime(); + + if (next==GHOST_kFireTimeNever) { + SleepTillEvent(m_display, -1); + } else { + SleepTillEvent(m_display, next - getMilliSeconds()); + } + } + + if (timerMgr->fireTimers(getMilliSeconds())) { + anyProcessed = true; + } + + while (XPending(m_display)) { + XEvent xevent; + XNextEvent(m_display, &xevent); + processEvent(&xevent); + anyProcessed = true; + } + + if (generateWindowExposeEvents()) { + anyProcessed = true; + } + } while (waitForEvent && !anyProcessed); + + return anyProcessed; +} + + void +GHOST_SystemX11:: +processEvent( + XEvent *xe +){ + GHOST_WindowX11 * window = findGhostWindow(xe->xany.window); + GHOST_Event * g_event = NULL; + + if (!window) { + return; + } + + switch (xe->type) { + case Expose: + { + XExposeEvent & xee = xe->xexpose; + + if (xee.count == 0) { + // Only generate a single expose event + // per read of the event queue. + + g_event = new + GHOST_Event( + getMilliSeconds(), + GHOST_kEventWindowUpdate, + window + ); + } + break; + } + case MotionNotify: + { + XMotionEvent &xme = xe->xmotion; + + g_event = new + GHOST_EventCursor( + getMilliSeconds(), + GHOST_kEventCursorMove, + window, + xme.x_root, + xme.y_root + ); + break; + } + + case KeyPress: + case KeyRelease: + { + XKeyEvent *xke = &(xe->xkey); + + KeySym key_sym = XLookupKeysym(xke,0); + char ascii; + + GHOST_TKey gkey = convertXKey(key_sym); + GHOST_TEventType type = (xke->type == KeyPress) ? + GHOST_kEventKeyDown : GHOST_kEventKeyUp; + + if (!XLookupString(xke, &ascii, 1, NULL, NULL)) { + ascii = '\0'; + } + + g_event = new + GHOST_EventKey( + getMilliSeconds(), + type, + window, + gkey, + ascii + ); + + break; + } + case ButtonPress: + case ButtonRelease: + { + + XButtonEvent & xbe = xe->xbutton; + GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft; + + switch (xbe.button) { + case Button1 : gbmask = GHOST_kButtonMaskLeft; break; + case Button3 : gbmask = GHOST_kButtonMaskRight; break; + default: + case Button2 : gbmask = GHOST_kButtonMaskMiddle; break; + } + + GHOST_TEventType type = (xbe.type == ButtonPress) ? + GHOST_kEventButtonDown : GHOST_kEventButtonUp; + + g_event = new + GHOST_EventButton( + getMilliSeconds(), + type, + window, + gbmask + ); + break; + } + + // change of size, border, layer etc. + case ConfigureNotify: + { + /* XConfigureEvent & xce = xe->xconfigure; */ + + g_event = new + GHOST_Event( + getMilliSeconds(), + GHOST_kEventWindowSize, + window + ); + break; + } + + case FocusIn: + case FocusOut: + { + XFocusChangeEvent &xfe = xe->xfocus; + + // May have to look at the type of event and filter some + // out. + + GHOST_TEventType gtype = (xfe.type == FocusIn) ? + GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate; + + g_event = new + GHOST_Event( + getMilliSeconds(), + gtype, + window + ); + break; + + } + case ClientMessage: + { + XClientMessageEvent & xcme = xe->xclient; + + if (xcme.data.l[0] == m_delete_window_atom) { + g_event = new + GHOST_Event( + getMilliSeconds(), + GHOST_kEventWindowClose, + window + ); + } else { + /* Unknown client message, ignore */ + } + + break; + } + + // We're not interested in the following things.(yet...) + case NoExpose : + case GraphicsExpose : + + case EnterNotify: + case LeaveNotify: + // XCrossingEvents pointer leave enter window. + break; + case MapNotify: + case UnmapNotify: + break; + case MappingNotify: + case ReparentNotify: + break; + + default: + break; + } + + if (g_event) { + pushEvent(g_event); + } +} + + + GHOST_TSuccess +GHOST_SystemX11:: +getModifierKeys( + GHOST_ModifierKeys& keys +) const { + + // analyse the masks retuned from XQueryPointer. + + memset(m_keyboard_vector,32,0); + + XQueryKeymap(m_display,m_keyboard_vector); + + // now translate key symobols into keycodes and + // test with vector. + + const KeyCode shift_l = XKeysymToKeycode(m_display,XK_Shift_L); + const KeyCode shift_r = XKeysymToKeycode(m_display,XK_Shift_R); + const KeyCode control_l = XKeysymToKeycode(m_display,XK_Control_L); + const KeyCode control_r = XKeysymToKeycode(m_display,XK_Control_R); + const KeyCode alt_l = XKeysymToKeycode(m_display,XK_Alt_L); + const KeyCode alt_r = XKeysymToKeycode(m_display,XK_Alt_R); + + // Shift + if ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) { + keys.set(GHOST_kModifierKeyLeftShift,true); + } else { + keys.set(GHOST_kModifierKeyLeftShift,false); + } + if ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) { + + keys.set(GHOST_kModifierKeyRightShift,true); + } else { + keys.set(GHOST_kModifierKeyRightShift,false); + } + + // control (weep) + if ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) { + keys.set(GHOST_kModifierKeyLeftControl,true); + } else { + keys.set(GHOST_kModifierKeyLeftControl,false); + } + if ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) { + keys.set(GHOST_kModifierKeyRightControl,true); + } else { + keys.set(GHOST_kModifierKeyRightControl,false); + } + + // Alt (yawn) + if ((m_keyboard_vector[alt_l >> 3] >> (alt_l & 7)) & 1) { + keys.set(GHOST_kModifierKeyLeftAlt,true); + } else { + keys.set(GHOST_kModifierKeyLeftAlt,false); + } + if ((m_keyboard_vector[alt_r >> 3] >> (alt_r & 7)) & 1) { + keys.set(GHOST_kModifierKeyRightAlt,true); + } else { + keys.set(GHOST_kModifierKeyRightAlt,false); + } + return GHOST_kSuccess; +} + + GHOST_TSuccess +GHOST_SystemX11:: +getButtons( + GHOST_Buttons& buttons +) const { + + Window root_return, child_return; + int rx,ry,wx,wy; + unsigned int mask_return; + + if (XQueryPointer( + m_display, + RootWindow(m_display,DefaultScreen(m_display)), + &root_return, + &child_return, + &rx,&ry, + &wx,&wy, + &mask_return + ) == False) { + return GHOST_kFailure; + } else { + + if (mask_return & Button1Mask) { + buttons.set(GHOST_kButtonMaskLeft,true); + } else { + buttons.set(GHOST_kButtonMaskLeft,false); + } + + if (mask_return & Button2Mask) { + buttons.set(GHOST_kButtonMaskMiddle,true); + } else { + buttons.set(GHOST_kButtonMaskMiddle,false); + } + + if (mask_return & Button3Mask) { + buttons.set(GHOST_kButtonMaskRight,true); + } else { + buttons.set(GHOST_kButtonMaskRight,false); + } + } + + return GHOST_kSuccess; +} + + + GHOST_TSuccess +GHOST_SystemX11:: +getCursorPosition( + GHOST_TInt32& x, + GHOST_TInt32& y +) const { + + Window root_return, child_return; + int rx,ry,wx,wy; + unsigned int mask_return; + + if (XQueryPointer( + m_display, + RootWindow(m_display,DefaultScreen(m_display)), + &root_return, + &child_return, + &rx,&ry, + &wx,&wy, + &mask_return + ) == False) { + return GHOST_kFailure; + } else { + x = rx; + y = ry; + } + return GHOST_kSuccess; +} + + + GHOST_TSuccess +GHOST_SystemX11:: +setCursorPosition( + GHOST_TInt32 x, + GHOST_TInt32 y +) const { + + // This is a brute force move in screen coordinates + // XWarpPointer does relative moves so first determine the + // current pointer position. + + int cx,cy; + if (getCursorPosition(cx,cy) == GHOST_kFailure) { + return GHOST_kFailure; + } + + int relx = x-cx; + int rely = y-cy; + + XWarpPointer(m_display,None,None,0,0,0,0,relx,rely); + XFlush(m_display); + + return GHOST_kSuccess; +} + + + void +GHOST_SystemX11:: +addDirtyWindow( + GHOST_WindowX11 * bad_wind +){ + + GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)"); + + m_dirty_windows.push_back(bad_wind); +} + + + bool +GHOST_SystemX11:: +generateWindowExposeEvents( +){ + + vector<GHOST_WindowX11 *>::iterator w_start = m_dirty_windows.begin(); + vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end(); + bool anyProcessed = false; + + for (;w_start != w_end; ++w_start) { + GHOST_Event * g_event = new + GHOST_Event( + getMilliSeconds(), + GHOST_kEventWindowUpdate, + *w_start + ); + + (*w_start)->validate(); + + if (g_event) { + pushEvent(g_event); + anyProcessed = true; + } + } + + m_dirty_windows.clear(); + return anyProcessed; +} + +#define GXMAP(k,x,y) case x: k = y; break; + + GHOST_TKey +GHOST_SystemX11:: +convertXKey( + unsigned int key +){ + GHOST_TKey type; + + if ((key >= XK_A) && (key <= XK_Z)) { + type = GHOST_TKey( key - XK_A + int(GHOST_kKeyA)); + } else if ((key >= XK_a) && (key <= XK_z)) { + type = GHOST_TKey(key - XK_a + int(GHOST_kKeyA)); + } else if ((key >= XK_0) && (key <= XK_9)) { + type = GHOST_TKey(key - XK_0 + int(GHOST_kKey0)); + } else if ((key >= XK_F1) && (key <= XK_F24)) { + type = GHOST_TKey(key - XK_F1 + int(GHOST_kKeyF1)); + } else { + switch(key) { + GXMAP(type,XK_BackSpace, GHOST_kKeyBackSpace); + GXMAP(type,XK_Tab, GHOST_kKeyTab); + GXMAP(type,XK_Return, GHOST_kKeyEnter); + GXMAP(type,XK_Escape, GHOST_kKeyEsc); + GXMAP(type,XK_space, GHOST_kKeySpace); + + GXMAP(type,XK_Linefeed, GHOST_kKeyLinefeed); + GXMAP(type,XK_semicolon, GHOST_kKeySemicolon); + GXMAP(type,XK_period, GHOST_kKeyPeriod); + GXMAP(type,XK_comma, GHOST_kKeyComma); + GXMAP(type,XK_quoteright, GHOST_kKeyQuote); + GXMAP(type,XK_quoteleft, GHOST_kKeyAccentGrave); + GXMAP(type,XK_minus, GHOST_kKeyMinus); + GXMAP(type,XK_slash, GHOST_kKeySlash); + GXMAP(type,XK_backslash, GHOST_kKeyBackslash); + GXMAP(type,XK_equal, GHOST_kKeyEqual); + GXMAP(type,XK_bracketleft, GHOST_kKeyLeftBracket); + GXMAP(type,XK_bracketright, GHOST_kKeyRightBracket); + GXMAP(type,XK_Pause, GHOST_kKeyPause); + + GXMAP(type,XK_Shift_L, GHOST_kKeyLeftShift); + GXMAP(type,XK_Shift_R, GHOST_kKeyRightShift); + GXMAP(type,XK_Control_L, GHOST_kKeyLeftControl); + GXMAP(type,XK_Control_R, GHOST_kKeyRightControl); + GXMAP(type,XK_Alt_L, GHOST_kKeyLeftAlt); + GXMAP(type,XK_Alt_R, GHOST_kKeyRightAlt); + + GXMAP(type,XK_Insert, GHOST_kKeyInsert); + GXMAP(type,XK_Delete, GHOST_kKeyDelete); + GXMAP(type,XK_Home, GHOST_kKeyHome); + GXMAP(type,XK_End, GHOST_kKeyEnd); + GXMAP(type,XK_Page_Up, GHOST_kKeyUpPage); + GXMAP(type,XK_Page_Down, GHOST_kKeyDownPage); + + GXMAP(type,XK_Left, GHOST_kKeyLeftArrow); + GXMAP(type,XK_Right, GHOST_kKeyRightArrow); + GXMAP(type,XK_Up, GHOST_kKeyUpArrow); + GXMAP(type,XK_Down, GHOST_kKeyDownArrow); + + GXMAP(type,XK_Caps_Lock, GHOST_kKeyCapsLock); + GXMAP(type,XK_Scroll_Lock, GHOST_kKeyScrollLock); + GXMAP(type,XK_Num_Lock, GHOST_kKeyNumLock); + + /* keypad events */ + + GXMAP(type,XK_KP_0, GHOST_kKeyNumpad0); + GXMAP(type,XK_KP_1, GHOST_kKeyNumpad1); + GXMAP(type,XK_KP_2, GHOST_kKeyNumpad2); + GXMAP(type,XK_KP_3, GHOST_kKeyNumpad3); + GXMAP(type,XK_KP_4, GHOST_kKeyNumpad4); + GXMAP(type,XK_KP_5, GHOST_kKeyNumpad5); + GXMAP(type,XK_KP_6, GHOST_kKeyNumpad6); + GXMAP(type,XK_KP_7, GHOST_kKeyNumpad7); + GXMAP(type,XK_KP_8, GHOST_kKeyNumpad8); + GXMAP(type,XK_KP_9, GHOST_kKeyNumpad9); + GXMAP(type,XK_KP_Decimal, GHOST_kKeyNumpadPeriod); + + GXMAP(type,XK_KP_Insert, GHOST_kKeyNumpad0); + GXMAP(type,XK_KP_End, GHOST_kKeyNumpad1); + GXMAP(type,XK_KP_Down, GHOST_kKeyNumpad2); + GXMAP(type,XK_KP_Page_Down, GHOST_kKeyNumpad3); + GXMAP(type,XK_KP_Left, GHOST_kKeyNumpad4); + GXMAP(type,XK_KP_Begin, GHOST_kKeyNumpad5); + GXMAP(type,XK_KP_Right, GHOST_kKeyNumpad6); + GXMAP(type,XK_KP_Home, GHOST_kKeyNumpad7); + GXMAP(type,XK_KP_Up, GHOST_kKeyNumpad8); + GXMAP(type,XK_KP_Page_Up, GHOST_kKeyNumpad9); + GXMAP(type,XK_KP_Delete, GHOST_kKeyNumpadPeriod); + + GXMAP(type,XK_KP_Enter, GHOST_kKeyNumpadEnter); + GXMAP(type,XK_KP_Add, GHOST_kKeyNumpadPlus); + GXMAP(type,XK_KP_Subtract, GHOST_kKeyNumpadMinus); + GXMAP(type,XK_KP_Multiply, GHOST_kKeyNumpadAsterisk); + GXMAP(type,XK_KP_Divide, GHOST_kKeyNumpadSlash); + + /* some extra sun cruft (NICE KEYBOARD!) */ +#ifdef __sun__ + GXMAP(type,0xffde, GHOST_kKeyNumpad1); + GXMAP(type,0xffe0, GHOST_kKeyNumpad3); + GXMAP(type,0xffdc, GHOST_kKeyNumpad5); + GXMAP(type,0xffd8, GHOST_kKeyNumpad7); + GXMAP(type,0xffda, GHOST_kKeyNumpad9); + + GXMAP(type,0xffd6, GHOST_kKeyNumpadSlash); + GXMAP(type,0xffd7, GHOST_kKeyNumpadAsterisk); +#endif + + default : + type = GHOST_kKeyUnknown; + break; + } + } + + return type; +} + +#undef GXMAP + + |