]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/gtkutil/cursor.cpp
Merge commit '01a950c3de3ef7f7da23360f925404e2bd385d5d' into master-merge
[xonotic/netradiant.git] / libs / gtkutil / cursor.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "cursor.h"
23
24 #include "stream/textstream.h"
25
26 #include <string.h>
27 #include <gdk/gdk.h>
28 #include <gtk/gtk.h>
29
30 // Note: NetRadiantCustom disables them but we still make use of them.
31 #if 1
32 GdkCursor* create_blank_cursor(){
33         return gdk_cursor_new(GDK_BLANK_CURSOR);
34 }
35
36 void set_cursor( ui::Widget widget, GdkCursorType cursor_type ){
37         GdkCursor* cursor = gdk_cursor_new( cursor_type );
38         gdk_window_set_cursor( gtk_widget_get_window(widget), cursor );
39         gdk_cursor_unref( cursor );
40 }
41
42 void blank_cursor( ui::Widget widget ){
43         set_cursor( widget, GDK_BLANK_CURSOR );
44 }
45
46 void default_cursor( ui::Widget widget ){
47         gdk_window_set_cursor( gtk_widget_get_window( widget ), NULL );
48 }
49 #endif
50
51 void Sys_GetCursorPos( ui::Widget widget, int *x, int *y ){
52         GdkDisplay *display = gtk_widget_get_display( GTK_WIDGET( widget ) );
53         // No need to store the screen, it will be recovered from widget again.
54         gdk_display_get_pointer( display, NULL, x, y, NULL );
55 }
56
57 void Sys_SetCursorPos( ui::Widget widget, int x, int y ){
58         GdkDisplay *display = gtk_widget_get_display( GTK_WIDGET( widget ) );
59         GdkScreen *screen = gtk_widget_get_screen( GTK_WIDGET( widget ) );
60         gdk_display_warp_pointer( display, screen, x, y );
61 }
62
63 gboolean DeferredMotion::gtk_motion(ui::Widget widget, GdkEventMotion *event, DeferredMotion *self)
64 {
65     self->motion( event->x, event->y, event->state );
66     return FALSE;
67 }
68
69 gboolean FreezePointer::motion_delta(ui::Widget widget, GdkEventMotion *event, FreezePointer *self)
70 {
71         /* FIXME: The pointer can be lost outside of the XY Window
72         or the Camera Window, see the comment in freeze_pointer function */
73         int current_x, current_y;
74         Sys_GetCursorPos( widget, &current_x, &current_y );
75         int dx = current_x - self->last_x;
76         int dy = current_y - self->last_y;
77         self->last_x = current_x;
78         self->last_y = current_y;
79         if ( dx != 0 || dy != 0 ) {
80                 //globalOutputStream() << "motion x: " << dx << ", y: " << dy << "\n";
81 #if defined(WORKAROUND_MACOS_GTK2_LAGGYPOINTER)
82                 ui::Dimensions dimensions = widget.dimensions();
83                 int window_x, window_y;
84                 int translated_x, translated_y;
85
86                 gdk_window_get_origin( gtk_widget_get_window( widget ), &window_x, &window_y);
87
88
89                 translated_x = current_x - ( window_x );
90                 translated_y = current_y - ( window_y );
91
92 #if 0
93                 int widget_x, widget_y;
94                 gtk_widget_translate_coordinates( GTK_WIDGET( widget ), gtk_widget_get_toplevel( GTK_WIDGET( widget ) ), 0, 0, &widget_x, &widget_y);
95
96                 globalOutputStream()
97                         << "window_x: " << window_x
98                         << ", window_y: " << window_y
99                         << ", widget_x: " << widget_x
100                         << ", widget_y: " << widget_y
101                         << ", current x: " << current_x
102                         << ", current_y: " << current_y
103                         << ", translated x: " << translated_x
104                         << ", translated_y: " << translated_y
105                         << ", width: " << dimensions.width
106                         << ", height: " << dimensions.height
107                         << "\n";
108 #endif
109
110                 if ( translated_x < 32 || translated_x > dimensions.width - 32
111                         || translated_y < 32 || translated_y > dimensions.height - 32 ) {
112 #if 0
113                         // Reposition the pointer to the widget center.
114                         int reposition_x = window_x + dimensions.width / 2;
115                         int reposition_y = window_y + dimensions.height / 2;
116 #else
117                         // Move the pointer to the opposite side of the XY Window
118                         // to maximize the distance that can be moved again.
119                         int reposition_x = current_x;
120                         int reposition_y = current_y;
121
122                         if ( translated_x < 32 ) {
123                                 reposition_x = window_x + dimensions.width - 32;
124                         }
125                         else if ( translated_x > dimensions.width - 32 ) {
126                                 reposition_x = window_x + 32;
127                         }
128
129                         if ( translated_y < 32 ) {
130                                 reposition_y = window_y + dimensions.height - 32;
131                         }
132                         else if ( translated_y > dimensions.height - 32 ) {
133                                 reposition_y = window_y + 32;
134                         }
135 #endif
136
137                         Sys_SetCursorPos( widget, reposition_x, reposition_y );
138                         self->last_x = reposition_x;
139                         self->last_y = reposition_y;
140                 }
141 #else
142                 int ddx = current_x - self->recorded_x;
143                 int ddy = current_y - self->recorded_y;
144                 if (ddx < -32 || ddx > 32 || ddy < -32 || ddy > 32) {
145                         Sys_SetCursorPos( widget, self->recorded_x, self->recorded_y );
146                         self->last_x = self->recorded_x;
147                         self->last_y = self->recorded_y;
148                 }
149 #endif
150                 self->m_function( dx, dy, event->state, self->m_data );
151         }
152         return FALSE;
153 }
154
155 void FreezePointer::freeze_pointer(ui::Widget widget, FreezePointer::MotionDeltaFunction function, void *data)
156 {
157         /* FIXME: This bug can happen if the pointer goes outside of the
158         XY Window while the right mouse button is not released,
159         the XY Window loses focus and can't read the right mouse button
160         release event and then cannot unfreeze the pointer, meaning the
161         user can attempt to freeze the pointer in another XY window.
162
163         This can happen with touch screen, especially those used to drive
164         virtual machine pointers, the cursor can be teleported outside of
165         the XY Window while maintaining pressure on the right mouse button.
166         This can also happen when the render is slow.
167
168         The bug also occurs with the Camera Window.
169
170         FIXME: It's would be possible to tell the user to save the map
171         at assert time before crashing because this bug does not corrupt
172         map saving. */
173         ASSERT_MESSAGE( m_function == 0, "can't freeze pointer" );
174
175         const GdkEventMask mask = static_cast<GdkEventMask>( GDK_POINTER_MOTION_MASK
176                                                                                                                  | GDK_POINTER_MOTION_HINT_MASK
177                                                                                                                  | GDK_BUTTON_MOTION_MASK
178                                                                                                                  | GDK_BUTTON1_MOTION_MASK
179                                                                                                                  | GDK_BUTTON2_MOTION_MASK
180                                                                                                                  | GDK_BUTTON3_MOTION_MASK
181                                                                                                                  | GDK_BUTTON_PRESS_MASK
182                                                                                                                  | GDK_BUTTON_RELEASE_MASK
183                                                                                                                  | GDK_VISIBILITY_NOTIFY_MASK );
184
185 #if defined(WORKAROUND_MACOS_GTK2_LAGGYPOINTER)
186         /* Keep the pointer visible during the move operation.
187         Because of a bug, it remains visible even if we give
188         the order to hide it anyway.
189         Other parts of the code assume the pointer is visible,
190         so make sure it is consistently visible accross
191         third-party updates that may fix the mouse pointer
192         visibility issue. */
193 #else
194         GdkCursor* cursor = create_blank_cursor();
195         //GdkGrabStatus status =
196         gdk_pointer_grab( gtk_widget_get_window( widget ), TRUE, mask, 0, cursor, GDK_CURRENT_TIME );
197         gdk_cursor_unref( cursor );
198 #endif
199
200         Sys_GetCursorPos( widget, &recorded_x, &recorded_y );
201
202         Sys_SetCursorPos( widget, recorded_x, recorded_y );
203
204         last_x = recorded_x;
205         last_y = recorded_y;
206
207         m_function = function;
208         m_data = data;
209
210         handle_motion = widget.connect( "motion_notify_event", G_CALLBACK( motion_delta ), this );
211 }
212
213 void FreezePointer::unfreeze_pointer(ui::Widget widget)
214 {
215         g_signal_handler_disconnect( G_OBJECT( widget ), handle_motion );
216
217         m_function = 0;
218         m_data = 0;
219
220 #if defined(WORKAROUND_MACOS_GTK2_LAGGYPOINTER)
221         /* The pointer was visible during all the move operation,
222         so, keep the current position. */
223 #else
224         // Note: NetRadiantCustom still uses window instead of widget.
225         Sys_SetCursorPos( widget, recorded_x, recorded_y );
226 #endif
227
228         gdk_pointer_ungrab( GDK_CURRENT_TIME );
229 }