1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup GHOST
22  * Declaration of GHOST_SystemX11 class.
23  */
24 
25 #pragma once
26 
27 #include <X11/XKBlib.h> /* allow detectable autorepeate */
28 #include <X11/Xlib.h>
29 
30 #include "../GHOST_Types.h"
31 #include "GHOST_System.h"
32 
33 // For tablets
34 #ifdef WITH_X11_XINPUT
35 #  include <X11/extensions/XInput.h>
36 
37 /* Disable xinput warp, currently not implemented by Xorg for multi-head display.
38  * (see comment in xserver "Xi/xiwarppointer.c" -> "FIXME: panoramix stuff is missing" ~ v1.13.4)
39  * If this is supported we can add back xinput for warping (fixing T48901).
40  * For now disable (see T50383). */
41 // #  define USE_X11_XINPUT_WARP
42 #endif
43 
44 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
45 #  define GHOST_X11_RES_NAME "Blender"  /* res_name */
46 #  define GHOST_X11_RES_CLASS "Blender" /* res_class */
47 #endif
48 
49 /* generic error handlers */
50 int GHOST_X11_ApplicationErrorHandler(Display *display, XErrorEvent *theEvent);
51 int GHOST_X11_ApplicationIOErrorHandler(Display *display);
52 
53 #define GHOST_X11_ERROR_HANDLERS_OVERRIDE(var) \
54   struct { \
55     XErrorHandler handler; \
56     XIOErrorHandler handler_io; \
57   } var = { \
58       XSetErrorHandler(GHOST_X11_ApplicationErrorHandler), \
59       XSetIOErrorHandler(GHOST_X11_ApplicationIOErrorHandler), \
60   }
61 
62 #define GHOST_X11_ERROR_HANDLERS_RESTORE(var) \
63   { \
64     (void)XSetErrorHandler(var.handler); \
65     (void)XSetIOErrorHandler(var.handler_io); \
66   } \
67   ((void)0)
68 
69 class GHOST_WindowX11;
70 
71 /**
72  * X11 Implementation of GHOST_System class.
73  * \see GHOST_System.
74  */
75 
76 class GHOST_SystemX11 : public GHOST_System {
77  public:
78   /**
79    * Constructor
80    * this class should only be instantiated by GHOST_ISystem.
81    */
82 
83   GHOST_SystemX11();
84 
85   /**
86    * Destructor.
87    */
88   ~GHOST_SystemX11();
89 
90   GHOST_TSuccess init();
91 
92   /**
93    * \section Interface Inherited from GHOST_ISystem
94    */
95 
96   /**
97    * Returns the system time.
98    * Returns the number of milliseconds since the start of the system process.
99    * \return The number of milliseconds.
100    */
101   GHOST_TUns64 getMilliSeconds() const;
102 
103   /**
104    * Returns the number of displays on this system.
105    * \return The number of displays.
106    */
107   GHOST_TUns8 getNumDisplays() const;
108 
109   /**
110    * Returns the dimensions of the main display on this system.
111    * \return The dimension of the main display.
112    */
113   void getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const;
114 
115   /**
116    * Returns the dimensions of all displays on this system.
117    * \return The dimension of the main display.
118    */
119   void getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const;
120 
121   /**
122    * Create a new window.
123    * The new window is added to the list of windows managed.
124    * Never explicitly delete the window, use disposeWindow() instead.
125    * \param   title   The name of the window
126    * (displayed in the title bar of the window if the OS supports it).
127    * \param   left        The coordinate of the left edge of the window.
128    * \param   top     The coordinate of the top edge of the window.
129    * \param   width       The width the window.
130    * \param   height      The height the window.
131    * \param   state       The state of the window when opened.
132    * \param   type        The type of drawing context installed in this window.
133    * \param   stereoVisual    Create a stereo visual for quad buffered stereo.
134    * \param   exclusive   Use to show the window ontop and ignore others
135    *                      (used fullscreen).
136    * \param   parentWindow    Parent (embedder) window
137    * \return  The new window (or 0 if creation failed).
138    */
139   GHOST_IWindow *createWindow(const char *title,
140                               GHOST_TInt32 left,
141                               GHOST_TInt32 top,
142                               GHOST_TUns32 width,
143                               GHOST_TUns32 height,
144                               GHOST_TWindowState state,
145                               GHOST_TDrawingContextType type,
146                               GHOST_GLSettings glSettings,
147                               const bool exclusive = false,
148                               const bool is_dialog = false,
149                               const GHOST_IWindow *parentWindow = 0);
150 
151   /**
152    * Create a new offscreen context.
153    * Never explicitly delete the context, use disposeContext() instead.
154    * \return  The new context (or 0 if creation failed).
155    */
156   GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings);
157 
158   /**
159    * Dispose of a context.
160    * \param   context Pointer to the context to be disposed.
161    * \return  Indication of success.
162    */
163   GHOST_TSuccess disposeContext(GHOST_IContext *context);
164 
165   /**
166    * Retrieves events from the system and stores them in the queue.
167    * \param waitForEvent Flag to wait for an event (or return immediately).
168    * \return Indication of the presence of events.
169    */
170   bool processEvents(bool waitForEvent);
171 
172   GHOST_TSuccess getCursorPosition(GHOST_TInt32 &x, GHOST_TInt32 &y) const;
173 
174   GHOST_TSuccess setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y);
175 
176   /**
177    * Returns the state of all modifier keys.
178    * \param keys  The state of all modifier keys (true == pressed).
179    * \return      Indication of success.
180    */
181   GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const;
182 
183   /**
184    * Returns the state of the mouse buttons (outside the message queue).
185    * \param buttons   The state of the buttons.
186    * \return          Indication of success.
187    */
188   GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const;
189 
190   /**
191    * Flag a window as dirty. This will
192    * generate a GHOST window update event on a call to processEvents()
193    */
194 
195   void addDirtyWindow(GHOST_WindowX11 *bad_wind);
196 
197   /**
198    * return a pointer to the X11 display structure
199    */
200 
getXDisplay()201   Display *getXDisplay()
202   {
203     return m_display;
204   }
205 
206 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
getX11_XIM()207   XIM getX11_XIM()
208   {
209     return m_xim;
210   }
211 #endif
212 
213   /** Helped function for get data from the clipboard. */
214   void getClipboard_xcout(const XEvent *evt,
215                           Atom sel,
216                           Atom target,
217                           unsigned char **txt,
218                           unsigned long *len,
219                           unsigned int *context) const;
220 
221   /**
222    * Returns unsigned char from CUT_BUFFER0
223    * \param selection     Get selection, X11 only feature
224    * \return              Returns the Clipboard indicated by Flag
225    */
226   GHOST_TUns8 *getClipboard(bool selection) const;
227 
228   /**
229    * Puts buffer to system clipboard
230    * \param buffer    The buffer to copy to the clipboard
231    * \param selection Set the selection into the clipboard, X11 only feature
232    */
233   void putClipboard(GHOST_TInt8 *buffer, bool selection) const;
234 
235   /**
236    * Show a system message box
237    * \param title                   The title of the message box
238    * \param message                 The message to display
239    * \param help_label              Help button label
240    * \param continue_label          Continue button label
241    * \param link                    An optional hyperlink
242    * \param dialog_options Options  how to display the message
243    */
244   GHOST_TSuccess showMessageBox(const char *title,
245                                 const char *message,
246                                 const char *help_label,
247                                 const char *continue_label,
248                                 const char *link,
249                                 GHOST_DialogOptions dialog_options) const;
250 #ifdef WITH_XDND
251   /**
252    * Creates a drag'n'drop event and pushes it immediately onto the event queue.
253    * Called by GHOST_DropTargetX11 class.
254    * \param eventType The type of drag'n'drop event
255    * \param draggedObjectType The type object concerned
256    * (currently array of file names, string, ?bitmap)
257    * \param mouseX x mouse coordinate (in window coordinates)
258    * \param mouseY y mouse coordinate
259    * \param window The window on which the event occurred
260    * \return Indication whether the event was handled.
261    */
262   static GHOST_TSuccess pushDragDropEvent(GHOST_TEventType eventType,
263                                           GHOST_TDragnDropTypes draggedObjectType,
264                                           GHOST_IWindow *window,
265                                           int mouseX,
266                                           int mouseY,
267                                           void *data);
268 #endif
269 
270   /**
271    * \see GHOST_ISystem
272    */
toggleConsole(int)273   int toggleConsole(int /*action*/)
274   {
275     return 0;
276   }
277 
278 #ifdef WITH_X11_XINPUT
279   typedef struct GHOST_TabletX11 {
280     GHOST_TTabletMode mode;
281     XDevice *Device;
282     XID ID;
283 
284     int MotionEvent;
285     int ProxInEvent;
286     int ProxOutEvent;
287     int PressEvent;
288 
289     int PressureLevels;
290     int XtiltLevels, YtiltLevels;
291   } GHOST_TabletX11;
292 
GetXTablets()293   std::vector<GHOST_TabletX11> &GetXTablets()
294   {
295     return m_xtablets;
296   }
297 #endif  // WITH_X11_XINPUT
298 
299   struct {
300     /**
301      * Atom used for ICCCM, WM-spec and Motif.
302      * We only need get this atom at the start, it's relative
303      * to the display not the window and are public for every
304      * window that need it.
305      */
306     Atom WM_STATE;
307     Atom WM_CHANGE_STATE;
308     Atom _NET_WM_STATE;
309     Atom _NET_WM_STATE_MAXIMIZED_HORZ;
310     Atom _NET_WM_STATE_MAXIMIZED_VERT;
311     Atom _NET_WM_STATE_FULLSCREEN;
312     Atom _MOTIF_WM_HINTS;
313     Atom WM_TAKE_FOCUS;
314     Atom WM_PROTOCOLS;
315     Atom WM_DELETE_WINDOW;
316 
317     /* Atoms for Selection, copy & paste. */
318     Atom TARGETS;
319     Atom STRING;
320     Atom COMPOUND_TEXT;
321     Atom TEXT;
322     Atom CLIPBOARD;
323     Atom PRIMARY;
324     Atom XCLIP_OUT;
325     Atom INCR;
326     Atom UTF8_STRING;
327 #ifdef WITH_X11_XINPUT
328     Atom TABLET;
329 #endif
330   } m_atom;
331 
332 #ifdef WITH_X11_XINPUT
333   XExtensionVersion m_xinput_version;
334 #endif
335 
336  private:
337   Display *m_display;
338 
339   /** Use for scan-code look-ups. */
340   XkbDescRec *m_xkb_descr;
341 
342 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
343   XIM m_xim;
344 #endif
345 
346 #ifdef WITH_X11_XINPUT
347   /* Tablet devices */
348   std::vector<GHOST_TabletX11> m_xtablets;
349 #endif
350 
351   /** The vector of windows that need to be updated. */
352   std::vector<GHOST_WindowX11 *> m_dirty_windows;
353 
354   /** Start time at initialization. */
355   GHOST_TUns64 m_start_time;
356 
357   /** A vector of keyboard key masks. */
358   char m_keyboard_vector[32];
359 
360   /**
361    * To prevent multiple warp, we store the time of the last warp event
362    * and stop accumulating all events generated before that.
363    */
364   Time m_last_warp_x;
365   Time m_last_warp_y;
366 
367   /* Detect auto-repeat glitch. */
368   unsigned int m_last_release_keycode;
369   Time m_last_release_time;
370 
371   uint m_keycode_last_repeat_key;
372 
373   /**
374    * Return the ghost window associated with the
375    * X11 window xwind
376    */
377 
378 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
379   bool openX11_IM();
380 #endif
381 
382 #ifdef WITH_X11_XINPUT
383   void clearXInputDevices();
384   void refreshXInputDevices();
385 #endif
386 
387   GHOST_WindowX11 *findGhostWindow(Window xwind) const;
388 
389   void processEvent(XEvent *xe);
390 
391   Time lastEventTime(Time default_time);
392 
393   bool generateWindowExposeEvents();
394 };
395