2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
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.
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.
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
24 #include "stream/textstream.h"
30 // Note: NetRadiantCustom disables them but we still make use of them.
32 /* Note: here is an alternative implementation,
33 it may be useful to try it on platforms were
34 gdk_cursor_new(GDK_BLANK_CURSOR)
37 GdkCursor* create_blank_cursor(){
40 char buffer [( 32 * 32 ) / 8];
41 memset( buffer, 0, ( 32 * 32 ) / 8 );
42 GdkColor white = {0, 0xffff, 0xffff, 0xffff};
43 GdkColor black = {0, 0x0000, 0x0000, 0x0000};
44 pixmap = gdk_bitmap_create_from_data( 0, buffer, 32, 32 );
45 mask = gdk_bitmap_create_from_data( 0, buffer, 32, 32 );
46 GdkCursor *cursor = gdk_cursor_new_from_pixmap( pixmap, mask, &white, &black, 1, 1 );
47 gdk_drawable_unref( pixmap );
48 gdk_drawable_unref( mask );
53 GdkCursor* create_blank_cursor(){
54 return gdk_cursor_new(GDK_BLANK_CURSOR);
57 void set_cursor( ui::Widget widget, GdkCursorType cursor_type ){
58 GdkCursor* cursor = gdk_cursor_new( cursor_type );
59 gdk_window_set_cursor( gtk_widget_get_window(widget), cursor );
60 gdk_cursor_unref( cursor );
63 void blank_cursor( ui::Widget widget ){
64 set_cursor( widget, GDK_BLANK_CURSOR );
67 void default_cursor( ui::Widget widget ){
68 gdk_window_set_cursor( gtk_widget_get_window( widget ), NULL );
72 void Sys_GetCursorPos( ui::Widget widget, int *x, int *y ){
73 GdkDisplay *display = gtk_widget_get_display( GTK_WIDGET( widget ) );
74 // No need to store the screen, it will be recovered from widget again.
75 gdk_display_get_pointer( display, NULL, x, y, NULL );
78 void Sys_SetCursorPos( ui::Widget widget, int x, int y ){
79 GdkDisplay *display = gtk_widget_get_display( GTK_WIDGET( widget ) );
80 GdkScreen *screen = gtk_widget_get_screen( GTK_WIDGET( widget ) );
81 gdk_display_warp_pointer( display, screen, x, y );
84 gboolean DeferredMotion::gtk_motion(ui::Widget widget, GdkEventMotion *event, DeferredMotion *self)
86 self->motion( event->x, event->y, event->state );
90 gboolean FreezePointer::motion_delta(ui::Widget widget, GdkEventMotion *event, FreezePointer *self)
92 /* FIXME: The pointer can be lost outside of the XY Window
93 or the Camera Window, see the comment in freeze_pointer function */
94 int current_x, current_y;
95 Sys_GetCursorPos( widget, ¤t_x, ¤t_y );
96 int dx = current_x - self->last_x;
97 int dy = current_y - self->last_y;
98 #if 0 // NetRadiantCustom
99 int ddx = current_x - self->center_x;
100 int ddy = current_y - self->center_y;
102 int ddx = current_x - self->recorded_x;
103 int ddy = current_y - self->recorded_y;
105 self->last_x = current_x;
106 self->last_y = current_y;
107 if ( dx != 0 || dy != 0 ) {
108 //globalOutputStream() << "motion x: " << dx << ", y: " << dy << "\n";
109 #if defined(WORKAROUND_MACOS_GTK2_LAGGYPOINTER)
110 ui::Dimensions dimensions = widget.dimensions();
111 int window_x, window_y;
112 int translated_x, translated_y;
114 gdk_window_get_origin( gtk_widget_get_window( widget ), &window_x, &window_y);
116 translated_x = current_x - ( window_x );
117 translated_y = current_y - ( window_y );
120 int widget_x, widget_y;
121 gtk_widget_translate_coordinates( GTK_WIDGET( widget ), gtk_widget_get_toplevel( GTK_WIDGET( widget ) ), 0, 0, &widget_x, &widget_y);
124 << "window_x: " << window_x
125 << ", window_y: " << window_y
126 << ", widget_x: " << widget_x
127 << ", widget_y: " << widget_y
128 << ", current x: " << current_x
129 << ", current_y: " << current_y
130 << ", translated x: " << translated_x
131 << ", translated_y: " << translated_y
132 << ", width: " << dimensions.width
133 << ", height: " << dimensions.height
137 if ( translated_x < 32 || translated_x > dimensions.width - 32
138 || translated_y < 32 || translated_y > dimensions.height - 32 ) {
140 // Reposition the pointer to the widget center.
141 int reposition_x = window_x + dimensions.width / 2;
142 int reposition_y = window_y + dimensions.height / 2;
144 // Move the pointer to the opposite side of the XY Window
145 // to maximize the distance that can be moved again.
146 int reposition_x = current_x;
147 int reposition_y = current_y;
149 if ( translated_x < 32 ) {
150 reposition_x = window_x + dimensions.width - 32;
152 else if ( translated_x > dimensions.width - 32 ) {
153 reposition_x = window_x + 32;
156 if ( translated_y < 32 ) {
157 reposition_y = window_y + dimensions.height - 32;
159 else if ( translated_y > dimensions.height - 32 ) {
160 reposition_y = window_y + 32;
164 Sys_SetCursorPos( widget, reposition_x, reposition_y );
165 self->last_x = reposition_x;
166 self->last_y = reposition_y;
169 if (ddx < -32 || ddx > 32 || ddy < -32 || ddy > 32) {
170 #if 0 // NetRadiantCustom
171 Sys_SetCursorPos( widget, self->center_x, self->center_y );
172 self->last_x = self->center_x;
173 self->last_y = self->center_y;
175 Sys_SetCursorPos( widget, self->recorded_x, self->recorded_y );
176 self->last_x = self->recorded_x;
177 self->last_y = self->recorded_y;
181 self->m_function( dx, dy, event->state, self->m_data );
186 /* NetRadiantCustom did this instead:
187 void FreezePointer::freeze_pointer(ui::Window window, ui::Widget widget, FreezePointer::MotionDeltaFunction function, void *data) */
188 void FreezePointer::freeze_pointer(ui::Widget widget, FreezePointer::MotionDeltaFunction function, void *data)
190 /* FIXME: This bug can happen if the pointer goes outside of the
191 XY Window while the right mouse button is not released,
192 the XY Window loses focus and can't read the right mouse button
193 release event and then cannot unfreeze the pointer, meaning the
194 user can attempt to freeze the pointer in another XY window.
196 This can happen with touch screen, especially those used to drive
197 virtual machine pointers, the cursor can be teleported outside of
198 the XY Window while maintaining pressure on the right mouse button.
199 This can also happen when the render is slow.
201 The bug also occurs with the Camera Window.
203 FIXME: It's would be possible to tell the user to save the map
204 at assert time before crashing because this bug does not corrupt
206 ASSERT_MESSAGE( m_function == 0, "can't freeze pointer" );
208 const GdkEventMask mask = static_cast<GdkEventMask>( GDK_POINTER_MOTION_MASK
209 | GDK_POINTER_MOTION_HINT_MASK
210 | GDK_BUTTON_MOTION_MASK
211 | GDK_BUTTON1_MOTION_MASK
212 | GDK_BUTTON2_MOTION_MASK
213 | GDK_BUTTON3_MOTION_MASK
214 | GDK_BUTTON_PRESS_MASK
215 | GDK_BUTTON_RELEASE_MASK
216 | GDK_VISIBILITY_NOTIFY_MASK );
218 #if defined(WORKAROUND_MACOS_GTK2_LAGGYPOINTER)
219 /* Keep the pointer visible during the move operation.
220 Because of a bug, it remains visible even if we give
221 the order to hide it anyway.
222 Other parts of the code assume the pointer is visible,
223 so make sure it is consistently visible accross
224 third-party updates that may fix the mouse pointer
227 #if 0 // NetRadiantCustom
228 //GdkCursor* cursor = create_blank_cursor();
229 GdkCursor* cursor = gdk_cursor_new( GDK_BLANK_CURSOR );
230 //GdkGrabStatus status =
231 /* fixes cursor runaways during srsly quick drags in camera
232 drags with pressed buttons have no problem at all w/o this */
233 gdk_pointer_grab( gtk_widget_get_window( widget ), TRUE, mask, 0, cursor, GDK_CURRENT_TIME );
234 //gdk_window_set_cursor ( GTK_WIDGET( window )->window, cursor );
235 /* is needed to fix activating neighbour widgets, that happens, if using upper one */
236 gtk_grab_add( widget );
239 GdkCursor* cursor = create_blank_cursor();
240 //GdkGrabStatus status =
241 gdk_pointer_grab( gtk_widget_get_window( widget ), TRUE, mask, 0, cursor, GDK_CURRENT_TIME );
242 gdk_cursor_unref( cursor );
246 Sys_GetCursorPos( widget, &recorded_x, &recorded_y );
248 #if 0 // NetRadiantCustom
249 /* using center for tracking for max safety */
250 gdk_window_get_origin( GTK_WIDGET( widget )->window, ¢er_x, ¢er_y );
251 auto allocation = widget.dimensions();
252 center_y += allocation.height / 2;
253 center_x += allocation.width / 2;
255 Sys_SetCursorPos( widget, center_x, center_y );
264 m_function = function;
267 handle_motion = widget.connect( "motion_notify_event", G_CALLBACK( motion_delta ), this );
270 void FreezePointer::unfreeze_pointer(ui::Widget widget)
272 g_signal_handler_disconnect( G_OBJECT( widget ), handle_motion );
277 #if defined(WORKAROUND_MACOS_GTK2_LAGGYPOINTER)
278 /* The pointer was visible during all the move operation,
279 so, keep the current position. */
281 // NetRadiantCustom still uses window instead of widget.
282 #if 0 // NetRadiantCustom
283 Sys_SetCursorPos( window, recorded_x, recorded_y );
285 Sys_SetCursorPos( widget, recorded_x, recorded_y );
289 // gdk_window_set_cursor( GTK_WIDGET( window )->window, 0 );
290 gdk_pointer_ungrab( GDK_CURRENT_TIME );
291 #if 0 // NetRadiantCustom
292 gtk_grab_remove( weedjet );