added MouseWheel event support for Silverlight 3.0
[moon.git] / src / window-gtk.cpp
blob7409a1ac535e6479295cf3f799ce0e41b61412d5
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * window-gtk.cpp: MoonWindow implementation using gtk widgets.
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007-2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include "window-gtk.h"
15 #include "deployment.h"
16 #include "timemanager.h"
18 MoonWindowGtk::MoonWindowGtk (bool fullscreen, int w, int h, MoonWindow *parent)
19 : MoonWindow (w, h)
21 this->fullscreen = fullscreen;
22 this->deployment = Deployment::GetCurrent ();
24 if (fullscreen)
25 InitializeFullScreen(parent);
26 else
27 InitializeNormal();
30 MoonWindowGtk::~MoonWindowGtk ()
32 /* gtk_widget_destroy can cause reentry (into another plugin if this destruction causes layout changes) */
33 DeploymentStack deployment_push_pop;
34 DisableEvents ();
35 if (widget != NULL)
36 gtk_widget_destroy (widget);
39 GdkWindow *
40 MoonWindowGtk::GetGdkWindow ()
42 GdkWindow *parent_window = gtk_widget_get_parent_window (widget);
43 if (parent_window == NULL)
44 parent_window = widget->window;
46 g_object_ref (parent_window);
47 return parent_window;
50 void
51 MoonWindowGtk::InitializeFullScreen (MoonWindow *parent)
53 widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
55 // only fullscreen on the monitor the plugin is on
56 GdkWindow *gdk = parent->GetGdkWindow ();
57 int monitor = gdk_screen_get_monitor_at_window (gdk_screen_get_default (), gdk);
58 GdkRectangle bounds;
59 gdk_screen_get_monitor_geometry (gdk_screen_get_default (), monitor, &bounds);
60 width = bounds.width;
61 height = bounds.height;
62 gtk_window_move (GTK_WINDOW (widget), bounds.x, bounds.y);
64 gtk_window_fullscreen (GTK_WINDOW (widget));
66 InitializeCommon ();
68 Show();
70 g_object_unref (gdk);
73 void
74 MoonWindowGtk::InitializeNormal ()
76 if (width == -1 || height == -1) {
77 g_warning ("you must specify width and height when creating a non-fullscreen gtk window");
78 width = 0;
79 height = 0;
82 widget = gtk_event_box_new ();
84 gtk_event_box_set_visible_window (GTK_EVENT_BOX (widget), false);
86 InitializeCommon ();
88 Show ();
91 void
92 MoonWindowGtk::InitializeCommon ()
94 // don't let gtk clear the window we'll do all the drawing.
95 //gtk_widget_set_app_paintable (widget, true);
96 gtk_widget_set_double_buffered (widget, false);
97 gtk_widget_set_size_request (widget, width, height);
99 g_signal_connect (widget, "size-allocate", G_CALLBACK (widget_size_allocate), this);
100 g_signal_connect (widget, "destroy", G_CALLBACK (widget_destroyed), this);
102 gtk_widget_add_events (widget,
103 GDK_POINTER_MOTION_MASK |
104 //GDK_POINTER_MOTION_HINT_MASK |
105 GDK_KEY_PRESS_MASK |
106 GDK_KEY_RELEASE_MASK |
107 GDK_BUTTON_PRESS_MASK |
108 GDK_BUTTON_RELEASE_MASK |
109 ((moonlight_flags & RUNTIME_INIT_DESKTOP_EXTENSIONS) != 0 ? GDK_SCROLL_MASK : 0) |
110 GDK_FOCUS_CHANGE_MASK);
112 GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
115 void
116 MoonWindowGtk::Resize (int width, int height)
118 gtk_widget_set_size_request (widget, width, height);
119 gtk_widget_queue_resize (widget);
122 /* XPM */
123 static const char *dot[] = {
124 "18 18 4 1",
125 " c None",
126 ". c #808080",
127 "+ c #303030",
128 "@ c #000000",
129 ".+. ",
130 "@@@ ",
131 ".@. ",
132 " ",
133 " ",
134 " ",
135 " ",
136 " ",
137 " ",
138 " ",
139 " ",
140 " ",
141 " ",
142 " ",
143 " ",
144 " ",
145 " ",
149 /* XPM */
150 static const char *eraser[] = {
151 "20 20 49 1",
152 " c None",
153 ". c #000000",
154 "+ c #858585",
155 "@ c #E8E8E8",
156 "# c #E9E9E9",
157 "$ c #E7E7E7",
158 "% c #E2E2E2",
159 "& c #D6D6D6",
160 "* c #7D7D7D",
161 "= c #565656",
162 "- c #E1E1E1",
163 "; c #E0E0E0",
164 "> c #DEDEDE",
165 ", c #DFDFDF",
166 "' c #474747",
167 ") c #6C6C6C",
168 "! c #B0B0B0",
169 "~ c #E3E3E3",
170 "{ c #4E4E4E",
171 "] c #636363",
172 "^ c #E6E6E6",
173 "/ c #505050",
174 "( c #4A4A4A",
175 "_ c #C7C7C7",
176 ": c #272727",
177 "< c #797979",
178 "[ c #E5E5E5",
179 "} c #DDDDDD",
180 "| c #9C9C9C",
181 "1 c #232323",
182 "2 c #E4E4E4",
183 "3 c #656565",
184 "4 c #313131",
185 "5 c #EAEAEA",
186 "6 c #ECECEC",
187 "7 c #EEEEEE",
188 "8 c #EFEFEF",
189 "9 c #F0F0F0",
190 "0 c #999999",
191 "a c #5D5D5D",
192 "b c #343434",
193 "c c #757575",
194 "d c #383838",
195 "e c #CECECE",
196 "f c #A9A9A9",
197 "g c #6F6F6F",
198 "h c #B3B3B3",
199 "i c #787878",
200 "j c #3F3F3F",
201 " ",
202 " ",
203 " ",
204 " ",
205 " ",
206 " ",
207 " ........... ",
208 " .+@#@@@$$%&*. ",
209 " =-;%>>>>>>>,' ",
210 " )!~>>>>>>>>>>{ ",
211 " ]^>>>>>>>>>,,/ ",
212 " (_;>>>>>>>>,>&: ",
213 " <[,}>>>>>>>-,| ",
214 " 1[-;>>>>>>>$2,3 ",
215 " 45678999998550a ",
216 " b~,,,,,,,,,;$c ",
217 " de-,,,,,,,,,fg ",
218 " bh%%,,;}}}>>ij ",
219 " ............ ",
223 void
224 MoonWindowGtk::SetBackgroundColor (Color *color)
226 GdkColor gdk_color;
227 gdk_color.red = color->r * 0xffff;
228 gdk_color.green = color->g * 0xffff;
229 gdk_color.blue = color->b * 0xffff;
231 gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, &gdk_color);
233 MoonWindow::SetBackgroundColor (color);
236 void
237 MoonWindowGtk::SetCursor (MouseCursor cursor)
239 if (widget->window) {
241 GdkCursor *c = NULL;
242 switch (cursor) {
243 case MouseCursorDefault:
244 c = NULL;
245 break;
246 case MouseCursorArrow:
247 c = gdk_cursor_new (GDK_LEFT_PTR);
248 break;
249 case MouseCursorHand:
250 c = gdk_cursor_new (GDK_HAND2);
251 break;
252 case MouseCursorWait:
253 c = gdk_cursor_new (GDK_WATCH);
254 break;
255 case MouseCursorIBeam:
256 c = gdk_cursor_new (GDK_XTERM);
257 break;
258 case MouseCursorStylus:
259 c = gdk_cursor_new_from_pixbuf (gdk_display_get_default (), gdk_pixbuf_new_from_xpm_data ((const char**) dot), 0, 0);
260 break;
261 case MouseCursorEraser:
262 c = gdk_cursor_new_from_pixbuf (gdk_display_get_default (), gdk_pixbuf_new_from_xpm_data ((const char**) eraser), 8, 8);
263 break;
264 case MouseCursorSizeNS:
265 c = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
266 break;
267 case MouseCursorSizeWE:
268 c = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
269 break;
270 case MouseCursorNone:
271 // Silverlight display no cursor if the enumeration value is invalid (e.g. -1)
272 default:
273 //from gdk-cursor doc :"To make the cursor invisible, use gdk_cursor_new_from_pixmap() to create a cursor with no pixels in it."
274 GdkPixmap *empty = gdk_bitmap_create_from_data (NULL, "0x00", 1, 1);
275 GdkColor empty_color = {0, 0, 0, 0};
276 c = gdk_cursor_new_from_pixmap (empty, empty, &empty_color, &empty_color, 0, 0);
277 g_object_unref (empty);
278 break;
282 gdk_window_set_cursor (widget->window, c);
284 if (c)
285 gdk_cursor_unref (c);
289 void
290 MoonWindowGtk::Invalidate (Rect r)
292 gtk_widget_queue_draw_area (widget,
293 (int) (widget->allocation.x + r.x),
294 (int) (widget->allocation.y + r.y),
295 (int) r.width, (int)r.height);
298 void
299 MoonWindowGtk::ProcessUpdates ()
301 if (widget->window)
302 gdk_window_process_updates (widget->window, false);
305 gboolean
306 MoonWindowGtk::HandleEvent (XEvent *event)
308 // nothing to do here, since we don't pump events into the gtk
309 // window, gtk calls our signal handlers directly.
310 return TRUE;
313 void
314 MoonWindowGtk::Show ()
316 gtk_widget_show (widget);
318 // The window has to be realized for this call to work
319 gtk_widget_set_extension_events (widget, GDK_EXTENSION_EVENTS_CURSOR);
320 /* we need to explicitly enable the devices */
321 for (GList *l = gdk_devices_list(); l; l = l->next) {
322 #if THIS_NOLONGER_BREAKS_LARRYS_MOUSE
323 GdkDevice *device = GDK_DEVICE(l->data);
324 //if (!device->has_cursor)
325 gdk_device_set_mode (device, GDK_MODE_SCREEN);
326 #endif
329 GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
332 void
333 MoonWindowGtk::Hide ()
335 gtk_widget_hide (widget);
338 void
339 MoonWindowGtk::EnableEvents (bool first)
341 g_signal_connect (widget, "expose-event", G_CALLBACK (expose_event), this);
342 g_signal_connect (widget, "motion-notify-event", G_CALLBACK (motion_notify), this);
343 g_signal_connect (widget, "enter-notify-event", G_CALLBACK (crossing_notify), this);
344 g_signal_connect (widget, "leave-notify-event", G_CALLBACK (crossing_notify), this);
345 g_signal_connect (widget, "key-press-event", G_CALLBACK (key_press), this);
346 g_signal_connect (widget, "key-release-event", G_CALLBACK (key_release), this);
347 g_signal_connect (widget, "button-press-event", G_CALLBACK (button_press), this);
348 g_signal_connect (widget, "button-release-event", G_CALLBACK (button_release), this);
349 g_signal_connect (widget, "scroll-event", G_CALLBACK (scroll), this);
350 g_signal_connect (widget, "focus-in-event", G_CALLBACK (focus_in), this);
351 g_signal_connect (widget, "focus-out-event", G_CALLBACK (focus_out), this);
353 if (first) {
354 g_signal_connect (widget, "realize", G_CALLBACK (realized), this);
355 g_signal_connect (widget, "unrealize", G_CALLBACK (unrealized), this);
357 if (GTK_WIDGET_REALIZED (widget))
358 realized (widget, this);
362 void
363 MoonWindowGtk::DisableEvents ()
365 g_signal_handlers_disconnect_matched (widget, G_SIGNAL_MATCH_DATA,
366 0, 0, NULL, NULL, this);
369 void
370 MoonWindowGtk::GrabFocus ()
372 gtk_widget_grab_focus (widget);
375 bool
376 MoonWindowGtk::HasFocus ()
378 return GTK_WIDGET_HAS_FOCUS (widget);
381 gboolean
382 MoonWindowGtk::expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer data)
384 MoonWindowGtk *window = (MoonWindowGtk*)data;
386 Deployment::SetCurrent (window->GetDeployment ());
388 if (!window->surface)
389 return true;
391 // we draw to a backbuffer pixmap, then transfer the contents
392 // to the widget's window.
393 GdkPixmap *pixmap = gdk_pixmap_new (widget->window,
394 MAX (event->area.width, 1), MAX (event->area.height, 1), -1);
396 window->surface->PaintToDrawable (pixmap,
397 gdk_drawable_get_visual (widget->window),
398 event,
399 widget->allocation.x,
400 widget->allocation.y,
401 window->GetTransparent (),
402 true);
404 GdkGC *gc = gdk_gc_new (pixmap);
406 gdk_gc_set_clip_region (gc, event->region);
408 gdk_draw_drawable (widget->window, gc, pixmap,
409 0, 0,
410 event->area.x, event->area.y,
411 event->area.width, event->area.height);
413 g_object_unref (pixmap);
414 g_object_unref (gc);
416 return true;
419 gboolean
420 MoonWindowGtk::button_press (GtkWidget *widget, GdkEventButton *event, gpointer data)
422 MoonWindowGtk *window = (MoonWindowGtk*)data;
424 Deployment::SetCurrent (window->GetDeployment ());
426 if (event->button != 1 && event->button != 3)
427 return false;
429 if (window->surface)
430 window->surface->HandleUIButtonPress (event);
432 // If we don't support right clicks (i.e. inside the browser)
433 // return false here
434 if (event->button == 3 && (moonlight_flags & RUNTIME_INIT_DESKTOP_EXTENSIONS) == 0)
435 return false;
437 // ignore HandleUIButtonPress's return value, and always
438 // return true here, or it gets bubbled up to firefox.
439 return true;
442 gboolean
443 MoonWindowGtk::button_release (GtkWidget *widget, GdkEventButton *event, gpointer data)
445 MoonWindowGtk *window = (MoonWindowGtk*)data;
447 Deployment::SetCurrent (window->GetDeployment ());
449 if (window->surface)
450 window->surface->HandleUIButtonRelease (event);
451 // ignore HandleUIButtonRelease's return value, and always
452 // return true here, or it gets bubbled up to firefox.
453 return true;
456 gboolean
457 MoonWindowGtk::scroll (GtkWidget *widget, GdkEventScroll *event, gpointer data)
459 MoonWindowGtk *window = (MoonWindowGtk*)data;
461 Deployment::SetCurrent (window->GetDeployment ());
463 if (window->surface)
464 window->surface->HandleUIScroll (event);
465 // ignore HandleUIScroll's return value, and always
466 // return true here, or it gets bubbled up to firefox.
467 return true;
470 gboolean
471 MoonWindowGtk::motion_notify (GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
473 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
475 Deployment::SetCurrent (window->GetDeployment ());
477 if (window->surface)
478 window->surface->HandleUIMotion (event);
479 // ignore HandleUIMotion's return value, and always
480 // return true here, or it gets bubbled up to firefox.
481 return true;
484 gboolean
485 MoonWindowGtk::crossing_notify (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
487 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
489 Deployment::SetCurrent (window->GetDeployment ());
491 if (window->surface) {
492 window->surface->HandleUICrossing (event);
493 return true;
496 return false;
499 gboolean
500 MoonWindowGtk::focus_in (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
502 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
504 Deployment::SetCurrent (window->GetDeployment ());
506 if (window->surface) {
507 window->surface->HandleUIFocusIn (event);
508 return true;
511 return false;
514 gboolean
515 MoonWindowGtk::focus_out (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
517 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
519 Deployment::SetCurrent (window->GetDeployment ());
521 if (window->surface) {
522 window->surface->HandleUIFocusOut (event);
523 return true;
526 return false;
529 gboolean
530 MoonWindowGtk::key_press (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
532 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
534 Deployment::SetCurrent (window->GetDeployment ());
536 if (window->surface) {
537 window->surface->HandleUIKeyPress (event);
538 return true;
541 return false;
544 gboolean
545 MoonWindowGtk::key_release (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
547 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
549 Deployment::SetCurrent (window->GetDeployment ());
551 if (window->surface) {
552 window->surface->HandleUIKeyRelease (event);
553 return true;
556 return false;
559 void
560 MoonWindowGtk::widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer data)
562 MoonWindowGtk *window = (MoonWindowGtk*)data;
564 Deployment::SetCurrent (window->GetDeployment ());
566 //printf ("Surface::size-allocate callback: current = %dx%d; new = %dx%d\n",
567 // s->width, s->height, allocation->width, allocation->height);
569 bool emit_resize = false;
571 if (window->width != allocation->width || window->height != allocation->height) {
572 window->width = allocation->width;
573 window->height = allocation->height;
575 emit_resize = true;
578 if (window->surface)
579 window->surface->HandleUIWindowAllocation (emit_resize);
582 void
583 MoonWindowGtk::widget_destroyed (GtkWidget *widget, gpointer user_data)
585 MoonWindowGtk* window = (MoonWindowGtk*)user_data;
587 window->widget = NULL;
588 if (window->surface)
589 window->surface->HandleUIWindowDestroyed (window);
592 gboolean
593 MoonWindowGtk::realized (GtkWidget *widget, gpointer user_data)
595 MoonWindowGtk* window = (MoonWindowGtk*)user_data;
597 #ifdef USE_XRANDR
598 #if INTEL_DRIVERS_STOP_SUCKING
599 // apparently the i965 drivers blank external screens when
600 // getting the screen info (um, ugh?). needless to say, this
601 // annoyance is worse than not using the monitor's refresh as
602 // the upper bound for our fps.
604 // http://lists.freedesktop.org/archives/xorg/2007-August/027616.html
605 int event_base, error_base;
606 GdkWindow *gdk_root = gtk_widget_get_root_window (widget);
607 Display *dpy = GDK_WINDOW_XDISPLAY(gdk_root);
608 Window root = GDK_WINDOW_XID (gdk_root);
609 if (XRRQueryExtension (dpy, &event_base, &error_base)) {
610 XRRScreenConfiguration *info = XRRGetScreenInfo (dpy,
611 root);
612 short rate = XRRConfigCurrentRate (info);
613 printf ("screen refresh rate = %d\n", rate);
614 if (window->surface)
615 window->surface->GetTimeManager()->SetMaximumRefreshRate (rate);
616 XRRFreeScreenConfigInfo (info);
618 #endif
619 #endif
621 Deployment::SetCurrent (window->GetDeployment ());
623 if (window->surface) {
624 window->surface->HandleUIWindowUnavailable ();
625 window->surface->HandleUIWindowAvailable ();
628 #if SANITY
629 Deployment::SetCurrent (NULL);
630 #endif
632 return true;
635 gboolean
636 MoonWindowGtk::unrealized (GtkWidget *widget, gpointer user_data)
638 MoonWindowGtk* window = (MoonWindowGtk*)user_data;
640 Deployment::SetCurrent (window->GetDeployment ());
642 if (window->surface)
643 window->surface->HandleUIWindowUnavailable ();
645 #if SANITY
646 Deployment::SetCurrent (NULL);
647 #endif
649 return true;