/** * clutter_win32_get_stage_from_window: * @hwnd: a window handle * * Gets the stage for a particular window. * * Return value: The stage or NULL if a stage does not exist for the * window. * * Since: 0.8 */ ClutterStage * clutter_win32_get_stage_from_window (HWND hwnd) { /* Check whether the window handle is an instance of the stage window class */ if ((ATOM) GetClassLongPtrW (hwnd, GCW_ATOM) == clutter_stage_win32_get_window_class ()) /* If it is there should be a pointer to the stage in the window extra data */ return CLUTTER_STAGE_WIN32 (GetWindowLongPtrW (hwnd, 0))->wrapper; else { /* Otherwise it might be a foreign window so we should check the stage list */ ClutterStageManager *stage_manager; const GSList *stages, *l; stage_manager = clutter_stage_manager_get_default (); stages = clutter_stage_manager_peek_stages (stage_manager); for (l = stages; l != NULL; l = l->next) { ClutterStage *stage = l->data; ClutterStageWindow *impl; impl = _clutter_stage_get_window (stage); g_assert (CLUTTER_IS_STAGE_WIN32 (impl)); if (CLUTTER_STAGE_WIN32 (impl)->hwnd == hwnd) return stage; } } return NULL; }
static void clutter_backend_win32_ensure_context (ClutterBackend *backend, ClutterStage *stage) { ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (_clutter_stage_get_window (stage)); cogl_set_framebuffer (COGL_FRAMEBUFFER (stage_win32->onscreen)); }
/** * clutter_win32_set_stage_foreign: * @stage: a #ClutterStage * @hwnd: an existing window handle * * Target the #ClutterStage to use an existing external window handle. * * Return value: %TRUE if foreign window is valid * * Since: 0.8 */ gboolean clutter_win32_set_stage_foreign (ClutterStage *stage, HWND hwnd) { ClutterStageWin32 *stage_win32; ClutterStageWindow *impl; ClutterActor *actor; RECT client_rect; ForeignWindowData fwd; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); g_return_val_if_fail (hwnd != NULL, FALSE); actor = CLUTTER_ACTOR (stage); impl = _clutter_stage_get_window (stage); stage_win32 = CLUTTER_STAGE_WIN32 (impl); if (!GetClientRect (hwnd, &client_rect)) { g_warning ("Unable to retrieve the new window geometry"); return FALSE; } fwd.stage_win32 = stage_win32; fwd.hwnd = hwnd; /* destroy the old HWND, if we have one and it's ours */ if (stage_win32->hwnd != NULL && !stage_win32->is_foreign_win) fwd.destroy_old_hwnd = TRUE; else fwd.destroy_old_hwnd = FALSE; fwd.geom.x = 0; fwd.geom.y = 0; fwd.geom.width = client_rect.right - client_rect.left; fwd.geom.height = client_rect.bottom - client_rect.top; _clutter_actor_rerealize (actor, set_foreign_window_callback, &fwd); /* Queue a relayout - so the stage will be allocated the new * window size. * * Note also that when the stage gets allocated the new * window size that will result in the stage's * priv->viewport being changed, which will in turn result * in the Cogl viewport changing when _clutter_do_redraw * calls _clutter_stage_maybe_setup_viewport(). */ clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); return TRUE; }
/** * clutter_x11_get_stage_window: (skip) * @stage: a #ClutterStage * * Gets the stages X Window. * * Return value: An XID for the stage window. * * */ Window clutter_x11_get_stage_window (ClutterStage *stage) { ClutterStageWindow *impl; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), None); impl = _clutter_stage_get_window (stage); g_assert (CLUTTER_IS_STAGE_X11 (impl)); return CLUTTER_STAGE_X11 (impl)->xwin; }
/** * clutter_win32_get_stage_window: * @stage: a #ClutterStage * * Gets the stage's window handle * * Return value: An HWND for the stage window. * * Since: 0.8 */ HWND clutter_win32_get_stage_window (ClutterStage *stage) { ClutterStageWindow *impl; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); impl = _clutter_stage_get_window (stage); g_return_val_if_fail (CLUTTER_IS_STAGE_WIN32 (impl), NULL); return CLUTTER_STAGE_WIN32 (impl)->hwnd; }
/** * clutter_wayland_stage_get_wl_shell_surface: (skip) * * @stage: a #ClutterStage * * Access the underlying data structure representing the shell surface that is * backing the #ClutterStage * * Note: this function can only be called when running on the Wayland * platform. Calling this function at any other time will return %NULL. * * Returns: (transfer non): the Wayland shell surface associated with * @stage * * Since: 1.10 */ struct wl_shell_surface * clutter_wayland_stage_get_wl_shell_surface (ClutterStage *stage) { ClutterStageWindow *stage_window = _clutter_stage_get_window (stage); ClutterStageWayland *stage_wayland; if (!CLUTTER_IS_STAGE_WAYLAND (stage_window)) return NULL; stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window); return stage_wayland->wayland_shell_surface; }
/** * clutter_win32_set_stage_foreign: * @stage: a #ClutterStage * @hwnd: an existing window handle * * Target the #ClutterStage to use an existing external window handle. * * Return value: %TRUE if foreign window is valid * * Since: 0.8 */ gboolean clutter_win32_set_stage_foreign (ClutterStage *stage, HWND hwnd) { ClutterStageWin32 *stage_win32; ClutterStageWindow *impl; ClutterActor *actor; RECT client_rect; POINT window_pos; ClutterGeometry geom; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); g_return_val_if_fail (hwnd != NULL, FALSE); actor = CLUTTER_ACTOR (stage); impl = _clutter_stage_get_window (stage); stage_win32 = CLUTTER_STAGE_WIN32 (impl); /* FIXME this needs updating to use _clutter_actor_rerealize(), * see the analogous code in x11 backend. Probably best if * win32 maintainer does it so they can be sure it compiles * and works. */ clutter_actor_unrealize (actor); if (!GetClientRect (hwnd, &client_rect)) { g_warning ("Unable to retrieve the new window geometry"); return FALSE; } window_pos.x = client_rect.left; window_pos.y = client_rect.right; ClientToScreen (hwnd, &window_pos); CLUTTER_NOTE (BACKEND, "Setting foreign window (0x%x)", (int) hwnd); stage_win32->hwnd = hwnd; stage_win32->is_foreign_win = TRUE; geom.x = 0; geom.y = 0; geom.width = client_rect.right - client_rect.left; geom.height = client_rect.bottom - client_rect.top; clutter_actor_set_geometry (actor, &geom); clutter_actor_realize (actor); return TRUE; }
/** * clutter_gdk_set_stage_foreign: * @stage: a #ClutterStage * @window: an existing #GdkWindow * * Target the #ClutterStage to use an existing external #GdkWindow * * Return value: %TRUE if foreign window is valid * * Since: 1.10 */ gboolean clutter_gdk_set_stage_foreign (ClutterStage *stage, GdkWindow *window) { ForeignWindowClosure closure; ClutterStageGdk *stage_gdk; ClutterStageWindow *impl; ClutterActor *actor; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); g_return_val_if_fail (!CLUTTER_ACTOR_IN_DESTRUCTION (stage), FALSE); g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE); impl = _clutter_stage_get_window (stage); if (!CLUTTER_IS_STAGE_GDK (impl)) { g_critical ("The Clutter backend is not a GDK backend"); return FALSE; } stage_gdk = CLUTTER_STAGE_GDK (impl); if (g_object_get_data (G_OBJECT (window), "clutter-stage-window") != NULL) { g_critical ("The provided GdkWindow is already in use by another ClutterStage"); return FALSE; } closure.stage_gdk = stage_gdk; closure.window = g_object_ref (window); actor = CLUTTER_ACTOR (stage); _clutter_actor_rerealize (actor, set_foreign_window_callback, &closure); /* Queue a relayout - so the stage will be allocated the new * window size. * * Note also that when the stage gets allocated the new * window size that will result in the stage's * priv->viewport being changed, which will in turn result * in the Cogl viewport changing when _clutter_do_redraw * calls _clutter_stage_maybe_setup_viewport(). */ clutter_actor_queue_relayout (actor); return TRUE; }
static void clutter_backend_egl_redraw (ClutterBackend *backend, ClutterStage *stage) { ClutterStageWindow *impl; impl = _clutter_stage_get_window (stage); if (!impl) return; g_assert (CLUTTER_IS_STAGE_EGL (impl)); clutter_stage_egl_redraw (CLUTTER_STAGE_EGL (impl), stage); }
/** * clutter_gdk_get_stage_window: * @stage: a #ClutterStage * * Gets the stages GdkWindow. * * Return value: (transfer none): A GdkWindow* for the stage window. * * Since: 1.10 */ GdkWindow * clutter_gdk_get_stage_window (ClutterStage *stage) { ClutterStageWindow *impl; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); impl = _clutter_stage_get_window (stage); if (!CLUTTER_IS_STAGE_GDK (impl)) { g_critical ("The Clutter backend is not a GDK backend"); return NULL; } return CLUTTER_STAGE_GDK (impl)->window; }
/* * FIXME: we should remove backend_class->redraw() and just * have stage_window_iface->redraw() */ static void clutter_backend_glx_redraw (ClutterBackend *backend, ClutterStage *stage) { ClutterStageWindow *impl = _clutter_stage_get_window (stage); if (G_UNLIKELY (impl == NULL)) { CLUTTER_NOTE (BACKEND, "Stage [%p] has no implementation", stage); return; } g_assert (CLUTTER_IS_STAGE_GLX (impl)); clutter_stage_glx_redraw (CLUTTER_STAGE_GLX (impl), stage); }
static void clutter_backend_glx_redraw (ClutterBackend *backend, ClutterStage *stage) { ClutterStageGLX *stage_glx; ClutterStageX11 *stage_x11; ClutterStageWindow *impl; impl = _clutter_stage_get_window (stage); if (G_UNLIKELY (impl == NULL)) { CLUTTER_NOTE (BACKEND, "Stage [%p] has no implementation", stage); return; } g_assert (CLUTTER_IS_STAGE_GLX (impl)); stage_x11 = CLUTTER_STAGE_X11 (impl); stage_glx = CLUTTER_STAGE_GLX (impl); /* this will cause the stage implementation to be painted */ clutter_actor_paint (CLUTTER_ACTOR (stage)); cogl_flush (); if (stage_x11->xwin != None) { /* wait for the next vblank */ CLUTTER_NOTE (BACKEND, "Waiting for vblank"); glx_wait_for_vblank (CLUTTER_BACKEND_GLX (backend)); /* push on the screen */ CLUTTER_NOTE (BACKEND, "glXSwapBuffers (display: %p, window: 0x%lx)", stage_x11->xdpy, (unsigned long) stage_x11->xwin); glXSwapBuffers (stage_x11->xdpy, stage_x11->xwin); } else { /* offscreen */ glXWaitGL (); CLUTTER_GLERR (); } }
static void clutter_backend_real_ensure_context (ClutterBackend *backend, ClutterStage *stage) { ClutterStageWindow *stage_impl; CoglFramebuffer *framebuffer; if (stage == NULL) return; stage_impl = _clutter_stage_get_window (stage); if (stage_impl == NULL) return; framebuffer = _clutter_stage_window_get_active_framebuffer (stage_impl); if (framebuffer == NULL) return; cogl_set_framebuffer (framebuffer); }
static void clutter_backend_egl_redraw (ClutterBackend *backend, ClutterStage *stage) { ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend); ClutterStageEGL *stage_egl; ClutterStageWindow *impl; impl = _clutter_stage_get_window (stage); if (!impl) return; g_assert (CLUTTER_IS_STAGE_EGL (impl)); stage_egl = CLUTTER_STAGE_EGL (impl); eglWaitNative (EGL_CORE_NATIVE_ENGINE); clutter_actor_paint (CLUTTER_ACTOR (stage)); cogl_flush (); eglWaitGL(); eglSwapBuffers (backend_egl->edpy, stage_egl->egl_surface); }
/* TODO: remove this interface in favour of * _clutter_stage_window_make_current () */ static void clutter_backend_glx_ensure_context (ClutterBackend *backend, ClutterStage *stage) { ClutterStageWindow *impl; /* if there is no stage, the stage is being destroyed or it has no * implementation attached to it then we clear the GL context */ if (stage == NULL || (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_IN_DESTRUCTION) || ((impl = _clutter_stage_get_window (stage)) == NULL)) { ClutterBackendX11 *backend_x11; backend_x11 = CLUTTER_BACKEND_X11 (backend); CLUTTER_NOTE (MULTISTAGE, "Clearing all context"); glXMakeContextCurrent (backend_x11->xdpy, None, None, NULL); } else { ClutterBackendGLX *backend_glx; ClutterBackendX11 *backend_x11; ClutterStageGLX *stage_glx; ClutterStageX11 *stage_x11; GLXDrawable drawable; g_assert (impl != NULL); stage_glx = CLUTTER_STAGE_GLX (impl); stage_x11 = CLUTTER_STAGE_X11 (impl); backend_glx = CLUTTER_BACKEND_GLX (backend); backend_x11 = CLUTTER_BACKEND_X11 (backend); drawable = stage_glx->glxwin ? stage_glx->glxwin : stage_x11->xwin; CLUTTER_NOTE (BACKEND, "Setting context for stage of type %s, window: 0x%x", G_OBJECT_TYPE_NAME (impl), (unsigned int) drawable); /* no GL context to set */ if (backend_glx->gl_context == None) return; clutter_x11_trap_x_errors (); /* we might get here inside the final dispose cycle, so we * need to handle this gracefully */ if (drawable == None) { GLXDrawable dummy_drawable; CLUTTER_NOTE (BACKEND, "Received a stale stage, clearing all context"); if (backend_glx->dummy_glxwin) dummy_drawable = backend_glx->dummy_glxwin; else dummy_drawable = backend_glx->dummy_xwin; if (dummy_drawable == None) glXMakeContextCurrent (backend_x11->xdpy, None, None, NULL); else { glXMakeContextCurrent (backend_x11->xdpy, dummy_drawable, dummy_drawable, backend_glx->gl_context); } } else { CLUTTER_NOTE (BACKEND, "MakeContextCurrent dpy: %p, window: 0x%x (%s), context: %p", backend_x11->xdpy, (unsigned int) drawable, stage_x11->is_foreign_xwin ? "foreign" : "native", backend_glx->gl_context); glXMakeContextCurrent (backend_x11->xdpy, drawable, drawable, backend_glx->gl_context); /* * In case we are using GLX_SGI_swap_control for vblank syncing we need call * glXSwapIntervalSGI here to make sure that it affects the current drawable. */ if (backend_glx->vblank_type == CLUTTER_VBLANK_GLX_SWAP && backend_glx->swap_interval != NULL) backend_glx->swap_interval (1); } if (clutter_x11_untrap_x_errors ()) g_critical ("Unable to make the stage window 0x%x the current " "GLX drawable", (unsigned int) drawable); } }
/** * clutter_x11_set_stage_foreign: * @stage: a #ClutterStage * @xwindow: an existing X Window id * * Target the #ClutterStage to use an existing external X Window * * Return value: %TRUE if foreign window is valid * * */ gboolean clutter_x11_set_stage_foreign (ClutterStage *stage, Window xwindow) { ClutterBackendX11 *backend_x11; ClutterStageX11 *stage_x11; ClutterStageCogl *stage_cogl; ClutterStageWindow *impl; ClutterActor *actor; gint x, y; guint width, height, border, depth; Window root_return; Status status; ForeignWindowData fwd; XVisualInfo *xvisinfo; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); g_return_val_if_fail (!CLUTTER_ACTOR_IN_DESTRUCTION (stage), FALSE); g_return_val_if_fail (xwindow != None, FALSE); impl = _clutter_stage_get_window (stage); stage_x11 = CLUTTER_STAGE_X11 (impl); stage_cogl = CLUTTER_STAGE_COGL (impl); backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend); xvisinfo = _clutter_backend_x11_get_visual_info (backend_x11); g_return_val_if_fail (xvisinfo != NULL, FALSE); clutter_x11_trap_x_errors (); status = XGetGeometry (backend_x11->xdpy, xwindow, &root_return, &x, &y, &width, &height, &border, &depth); if (clutter_x11_untrap_x_errors () || !status) { g_critical ("Unable to retrieve the geometry of the foreign window: " "XGetGeometry() failed (status code: %d)", status); return FALSE; } if (width == 0 || height == 0) { g_warning ("The size of the foreign window is 0x0"); return FALSE; } if (depth != xvisinfo->depth) { g_warning ("The depth of the visual of the foreign window is %d, but " "Clutter has been initialized to require a visual depth " "of %d", depth, xvisinfo->depth); return FALSE; } fwd.stage_x11 = stage_x11; fwd.xwindow = xwindow; /* destroy the old Window, if we have one and it's ours */ if (stage_x11->xwin != None && !stage_x11->is_foreign_xwin) fwd.destroy_old_xwindow = TRUE; else fwd.destroy_old_xwindow = FALSE; fwd.geom.x = x; fwd.geom.y = y; fwd.geom.width = width; fwd.geom.height = height; actor = CLUTTER_ACTOR (stage); _clutter_actor_rerealize (actor, set_foreign_window_callback, &fwd); /* Queue a relayout - so the stage will be allocated the new * window size. * * Note also that when the stage gets allocated the new * window size that will result in the stage's * priv->viewport being changed, which will in turn result * in the Cogl viewport changing when _clutter_do_redraw * calls _clutter_stage_maybe_setup_viewport(). */ clutter_actor_queue_relayout (actor); return TRUE; }
static void clutter_backend_glx_ensure_context (ClutterBackend *backend, ClutterStage *stage) { ClutterStageWindow *impl; /* if there is no stage, the stage is being destroyed or it has no * implementation attached to it then we clear the GL context */ if (stage == NULL || (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_IN_DESTRUCTION) || ((impl = _clutter_stage_get_window (stage)) == NULL)) { ClutterBackendX11 *backend_x11; backend_x11 = CLUTTER_BACKEND_X11 (backend); CLUTTER_NOTE (MULTISTAGE, "Clearing all context"); glXMakeCurrent (backend_x11->xdpy, None, NULL); } else { ClutterBackendGLX *backend_glx; ClutterStageGLX *stage_glx; ClutterStageX11 *stage_x11; g_assert (impl != NULL); CLUTTER_NOTE (MULTISTAGE, "Setting context for stage of type %s [%p]", g_type_name (G_OBJECT_TYPE (impl)), impl); stage_glx = CLUTTER_STAGE_GLX (impl); stage_x11 = CLUTTER_STAGE_X11 (impl); backend_glx = CLUTTER_BACKEND_GLX (backend); /* no GL context to set */ if (backend_glx->gl_context == None) return; clutter_x11_trap_x_errors (); /* we might get here inside the final dispose cycle, so we * need to handle this gracefully */ if (stage_x11->xwin == None) { ClutterBackendX11 *backend_x11; backend_x11 = CLUTTER_BACKEND_X11 (backend); CLUTTER_NOTE (MULTISTAGE, "Received a stale stage, clearing all context"); glXMakeCurrent (backend_x11->xdpy, None, NULL); } else { CLUTTER_NOTE (BACKEND, "MakeCurrent dpy: %p, window: 0x%x (%s), context: %p", stage_x11->xdpy, (int) stage_x11->xwin, stage_x11->is_foreign_xwin ? "foreign" : "native", backend_glx->gl_context); glXMakeCurrent (stage_x11->xdpy, stage_x11->xwin, backend_glx->gl_context); } if (clutter_x11_untrap_x_errors ()) g_critical ("Unable to make the stage window 0x%x the current " "GLX drawable", (int) stage_x11->xwin); } }
static gboolean event_translate (ClutterBackend *backend, ClutterEvent *event, XEvent *xevent) { ClutterBackendX11 *backend_x11; ClutterStageX11 *stage_x11; ClutterStage *stage; ClutterStageWindow *impl; ClutterDeviceManager *manager; gboolean res, not_yet_handled = FALSE; Window xwindow, stage_xwindow; ClutterInputDevice *device; backend_x11 = CLUTTER_BACKEND_X11 (backend); xwindow = xevent->xany.window; if (backend_x11->event_filters) { GSList *node; node = backend_x11->event_filters; while (node) { ClutterX11EventFilter *filter = node->data; switch (filter->func (xevent, event, filter->data)) { case CLUTTER_X11_FILTER_CONTINUE: break; case CLUTTER_X11_FILTER_TRANSLATE: return TRUE; case CLUTTER_X11_FILTER_REMOVE: return FALSE; default: break; } node = node->next; } } /* Do further processing only on events for the stage window (the x11 * filters might be getting events for other windows, so do not mess * them about. */ stage = clutter_x11_get_stage_from_window (xwindow); if (stage == NULL) return FALSE; impl = _clutter_stage_get_window (stage); stage_x11 = CLUTTER_STAGE_X11 (impl); stage_xwindow = xwindow; /* clutter_x11_get_stage_window (stage); */ event->any.stage = stage; res = TRUE; update_last_event_time (backend_x11, xevent); manager = clutter_device_manager_get_default (); switch (xevent->type) { case ConfigureNotify: if (!stage_x11->is_foreign_xwin) { CLUTTER_NOTE (BACKEND, "%s: ConfigureNotify[%x] (%d, %d)", G_STRLOC, (unsigned int) stage_x11->xwin, xevent->xconfigure.width, xevent->xconfigure.height); /* Queue a relayout - we want glViewport to be called * with the correct values, and this is done in ClutterStage * via _cogl_onscreen_clutter_backend_set_size (). * * We queue a relayout, because if this ConfigureNotify is * in response to a size we set in the application, the * set_size above is essentially a null-op. * * Make sure we do this only when the size has changed, * otherwise we end up relayouting on window moves. */ if ((stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN) || (stage_x11->xwin_width != xevent->xconfigure.width) || (stage_x11->xwin_height != xevent->xconfigure.height)) clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); /* If we're fullscreened, we want these variables to * represent the size of the window before it was set * to fullscreen. */ if (!(stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN)) { stage_x11->xwin_width = xevent->xconfigure.width; stage_x11->xwin_height = xevent->xconfigure.height; } clutter_actor_set_size (CLUTTER_ACTOR (stage), xevent->xconfigure.width, xevent->xconfigure.height); CLUTTER_UNSET_PRIVATE_FLAGS (stage_x11->wrapper, CLUTTER_STAGE_IN_RESIZE); /* the resize process is complete, so we can ask the stage * to set up the GL viewport with the new size */ clutter_stage_ensure_viewport (stage); } res = FALSE; break; case PropertyNotify: if (xevent->xproperty.atom == backend_x11->atom_NET_WM_STATE && xevent->xproperty.window == stage_xwindow && !stage_x11->is_foreign_xwin) { Atom type; gint format; gulong n_items, bytes_after; guchar *data = NULL; gboolean fullscreen_set = FALSE; clutter_x11_trap_x_errors (); XGetWindowProperty (backend_x11->xdpy, stage_xwindow, backend_x11->atom_NET_WM_STATE, 0, G_MAXLONG, False, XA_ATOM, &type, &format, &n_items, &bytes_after, &data); clutter_x11_untrap_x_errors (); if (type != None && data != NULL) { Atom *atoms = (Atom *) data; gulong i; gboolean is_fullscreen = FALSE; for (i = 0; i < n_items; i++) { if (atoms[i] == backend_x11->atom_NET_WM_STATE_FULLSCREEN) fullscreen_set = TRUE; } is_fullscreen = (stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN); if (fullscreen_set != is_fullscreen) { if (fullscreen_set) stage_x11->state |= CLUTTER_STAGE_STATE_FULLSCREEN; else stage_x11->state &= ~CLUTTER_STAGE_STATE_FULLSCREEN; stage_x11->fullscreening = fullscreen_set; event->type = CLUTTER_STAGE_STATE; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_FULLSCREEN; event->stage_state.new_state = stage_x11->state; } else res = FALSE; XFree (data); } else res = FALSE; } else res = FALSE; break; case MapNotify: res = FALSE; break; case UnmapNotify: res = FALSE; break; case FocusIn: if (!(stage_x11->state & CLUTTER_STAGE_STATE_ACTIVATED)) { /* TODO: check xevent->xfocus.detail ? */ stage_x11->state |= CLUTTER_STAGE_STATE_ACTIVATED; event->type = CLUTTER_STAGE_STATE; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_x11->state; } else res = FALSE; break; case FocusOut: if (stage_x11->state & CLUTTER_STAGE_STATE_ACTIVATED) { stage_x11->state &= ~CLUTTER_STAGE_STATE_ACTIVATED; event->type = CLUTTER_STAGE_STATE; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_x11->state; } else res = FALSE; break; case Expose: { CLUTTER_NOTE (MULTISTAGE, "expose for stage: %p, redrawing", stage); clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); res = FALSE; } break; case DestroyNotify: CLUTTER_NOTE (EVENT, "destroy notify:\txid: %ld", xevent->xdestroywindow.window); if (xevent->xdestroywindow.window == stage_xwindow && !stage_x11->is_foreign_xwin) event->type = event->any.type = CLUTTER_DESTROY_NOTIFY; else res = FALSE; break; case ClientMessage: CLUTTER_NOTE (EVENT, "client message"); event->type = event->any.type = CLUTTER_CLIENT_MESSAGE; if (xevent->xclient.message_type == backend_x11->atom_XEMBED) res = handle_xembed_event (backend_x11, xevent); else if (xevent->xclient.message_type == backend_x11->atom_WM_PROTOCOLS) { res = handle_wm_protocols_event (backend_x11, stage_xwindow, xevent); event->type = event->any.type = CLUTTER_DELETE; } break; case KeyPress: event->key.type = event->type = CLUTTER_KEY_PRESS; event->key.device = clutter_device_manager_get_core_device (manager, CLUTTER_KEYBOARD_DEVICE); translate_key_event (backend, event, xevent); set_user_time (backend_x11, &xwindow, xevent->xkey.time); break; case KeyRelease: /* old-style X11 terminals require that even modern X11 send * KeyPress/KeyRelease pairs when auto-repeating. for this * reason modern(-ish) API like XKB has a way to detect * auto-repeat and do a single KeyRelease at the end of a * KeyPress sequence. * * this check emulates XKB's detectable auto-repeat; we peek * the next event and check if it's a KeyPress for the same key * and timestamp - and then ignore it if it matches the * KeyRelease */ if (XPending (xevent->xkey.display)) { XEvent next_event; XPeekEvent (xevent->xkey.display, &next_event); if (next_event.type == KeyPress && next_event.xkey.keycode == xevent->xkey.keycode && next_event.xkey.time == xevent->xkey.time) { res = FALSE; break; } } event->key.type = event->type = CLUTTER_KEY_RELEASE; event->key.device = clutter_device_manager_get_core_device (manager, CLUTTER_KEYBOARD_DEVICE); translate_key_event (backend, event, xevent); break; default: /* ignore every other event */ not_yet_handled = TRUE; break; } /* Input device event handling.. */ if (not_yet_handled) { device = clutter_device_manager_get_core_device (manager, CLUTTER_POINTER_DEVICE); /* Regular X event */ switch (xevent->type) { case ButtonPress: switch (xevent->xbutton.button) { case 4: /* up */ case 5: /* down */ case 6: /* left */ case 7: /* right */ event->scroll.type = event->type = CLUTTER_SCROLL; if (xevent->xbutton.button == 4) event->scroll.direction = CLUTTER_SCROLL_UP; else if (xevent->xbutton.button == 5) event->scroll.direction = CLUTTER_SCROLL_DOWN; else if (xevent->xbutton.button == 6) event->scroll.direction = CLUTTER_SCROLL_LEFT; else event->scroll.direction = CLUTTER_SCROLL_RIGHT; event->scroll.time = xevent->xbutton.time; event->scroll.x = xevent->xbutton.x; event->scroll.y = xevent->xbutton.y; event->scroll.modifier_state = xevent->xbutton.state; event->scroll.device = device; break; default: event->button.type = event->type = CLUTTER_BUTTON_PRESS; event->button.time = xevent->xbutton.time; event->button.x = xevent->xbutton.x; event->button.y = xevent->xbutton.y; event->button.modifier_state = xevent->xbutton.state; event->button.button = xevent->xbutton.button; event->button.device = device; break; } set_user_time (backend_x11, &xwindow, event->button.time); res = TRUE; break; case ButtonRelease: /* scroll events don't have a corresponding release */ if (xevent->xbutton.button == 4 || xevent->xbutton.button == 5 || xevent->xbutton.button == 6 || xevent->xbutton.button == 7) { res = FALSE; goto out; } event->button.type = event->type = CLUTTER_BUTTON_RELEASE; event->button.time = xevent->xbutton.time; event->button.x = xevent->xbutton.x; event->button.y = xevent->xbutton.y; event->button.modifier_state = xevent->xbutton.state; event->button.button = xevent->xbutton.button; event->button.device = device; res = TRUE; break; case MotionNotify: event->motion.type = event->type = CLUTTER_MOTION; event->motion.time = xevent->xmotion.time; event->motion.x = xevent->xmotion.x; event->motion.y = xevent->xmotion.y; event->motion.modifier_state = xevent->xmotion.state; event->motion.device = device; res = TRUE; break; case EnterNotify: /* we know that we are entering the stage here */ _clutter_input_device_set_stage (device, stage); CLUTTER_NOTE (EVENT, "Entering the stage"); /* Convert enter notifies to motion events because X doesn't emit the corresponding motion notify */ event->motion.type = event->type = CLUTTER_MOTION; event->motion.time = xevent->xcrossing.time; event->motion.x = xevent->xcrossing.x; event->motion.y = xevent->xcrossing.y; event->motion.modifier_state = xevent->xcrossing.state; event->motion.source = CLUTTER_ACTOR (stage); event->motion.device = device; res = TRUE; break; case LeaveNotify: if (device->stage == NULL) { CLUTTER_NOTE (EVENT, "Discarding LeaveNotify for ButtonRelease " "event off-stage"); res = FALSE; goto out; } /* we know that we are leaving the stage here */ _clutter_input_device_set_stage (device, NULL); CLUTTER_NOTE (EVENT, "Leaving the stage (time:%u)", event->crossing.time); event->crossing.type = event->type = CLUTTER_LEAVE; event->crossing.time = xevent->xcrossing.time; event->crossing.x = xevent->xcrossing.x; event->crossing.y = xevent->xcrossing.y; event->crossing.source = CLUTTER_ACTOR (stage); event->crossing.device = device; res = TRUE; break; default: res = FALSE; break; } } /* XInput fun...*/ if (!res && clutter_x11_has_xinput ()) { #ifdef HAVE_XINPUT int *ev_types = backend_x11->event_types; int button_press, button_release; int key_press, key_release; int motion_notify; button_press = ev_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT]; button_release = ev_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT]; motion_notify = ev_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT]; key_press = ev_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT]; key_release = ev_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT]; CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type); if (xevent->type == button_press) { XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent; device = _clutter_x11_get_device_for_xid (xbev->deviceid); _clutter_input_device_set_stage (device, stage); CLUTTER_NOTE (EVENT, "XI ButtonPress for %li ('%s') at %d, %d", xbev->deviceid, device->device_name, xbev->x, xbev->y); switch (xbev->button) { case 4: case 5: case 6: case 7: event->scroll.type = event->type = CLUTTER_SCROLL; if (xbev->button == 4) event->scroll.direction = CLUTTER_SCROLL_UP; else if (xbev->button == 5) event->scroll.direction = CLUTTER_SCROLL_DOWN; else if (xbev->button == 6) event->scroll.direction = CLUTTER_SCROLL_LEFT; else event->scroll.direction = CLUTTER_SCROLL_RIGHT; event->scroll.time = xbev->time; event->scroll.x = xbev->x; event->scroll.y = xbev->y; event->scroll.modifier_state = xbev->state; event->scroll.device = device; break; default: event->button.type = event->type = CLUTTER_BUTTON_PRESS; event->button.time = xbev->time; event->button.x = xbev->x; event->button.y = xbev->y; event->button.modifier_state = xbev->state; event->button.button = xbev->button; event->button.device = device; break; } set_user_time (backend_x11, &xwindow, xbev->time); res = TRUE; } else if (xevent->type == button_release) { XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent; device = _clutter_x11_get_device_for_xid (xbev->deviceid); _clutter_input_device_set_stage (device, stage); CLUTTER_NOTE (EVENT, "XI ButtonRelease for %li ('%s') at %d, %d", xbev->deviceid, device->device_name, xbev->x, xbev->y); /* scroll events don't have a corresponding release */ if (xbev->button == 4 || xbev->button == 5 || xbev->button == 6 || xbev->button == 7) { res = FALSE; goto out; } event->button.type = event->type = CLUTTER_BUTTON_RELEASE; event->button.time = xbev->time; event->button.x = xbev->x; event->button.y = xbev->y; event->button.modifier_state = xbev->state; event->button.button = xbev->button; event->button.device = device; res = TRUE; } else if (xevent->type == motion_notify) { XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent; device = _clutter_x11_get_device_for_xid (xmev->deviceid); _clutter_input_device_set_stage (device, stage); CLUTTER_NOTE (EVENT, "XI Motion for %li ('%s') at %d, %d", xmev->deviceid, device->device_name, xmev->x, xmev->y); event->motion.type = event->type = CLUTTER_MOTION; event->motion.time = xmev->time; event->motion.x = xmev->x; event->motion.y = xmev->y; event->motion.modifier_state = xmev->state; event->motion.device = device; res = TRUE; } else if (xevent->type == key_press || xevent->type == key_release) { /* the XInput 1.x handling of key presses/releases is broken: * it makes key repeat, key presses and releases outside the * window not generate events even when the window has focus */ XDeviceKeyEvent *xkev = (XDeviceKeyEvent *) xevent; XEvent xevent_converted; convert_xdevicekey_to_xkey (xkev, &xevent_converted); event->key.type = event->type = (xevent->type == key_press) ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE; translate_key_event (backend, event, &xevent_converted); if (xevent->type == key_press) set_user_time (backend_x11, &xwindow, xkev->time); } else #endif /* HAVE_XINPUT */ { CLUTTER_NOTE (EVENT, "Uknown Event"); res = FALSE; } } out: return res; }
/** * clutter_win32_handle_event: * @msg: A pointer to a structure describing a Win32 message. * * This function processes a single Win32 message. It can be used to * hook into external windows message processing (for example, a GDK * filter function). * * If clutter_win32_disable_event_retrieval() has been called, you must * let this function process events to update Clutter's internal state. * * Return value: %TRUE if the message was handled entirely by Clutter * and no further processing (such as calling the default window * procedure) should take place. %FALSE is returned if is the message * was not handled at all or if Clutter expects processing to take * place. * * Since: 1.6 */ gboolean clutter_win32_handle_event (const MSG *msg) { ClutterBackendWin32 *backend_win32; ClutterStageWin32 *stage_win32; ClutterDeviceManager *manager; ClutterInputDevice *core_pointer, *core_keyboard; ClutterStage *stage; ClutterStageWindow *impl; gboolean return_value = FALSE; stage = clutter_win32_get_stage_from_window (msg->hwnd); /* Ignore any messages for windows which we don't have a stage for */ if (stage == NULL) return FALSE; impl = _clutter_stage_get_window (stage); stage_win32 = CLUTTER_STAGE_WIN32 (impl); backend_win32 = stage_win32->backend; manager = clutter_device_manager_get_default (); core_pointer = clutter_device_manager_get_core_device (manager, CLUTTER_POINTER_DEVICE); core_keyboard = clutter_device_manager_get_core_device (manager, CLUTTER_KEYBOARD_DEVICE); switch (msg->message) { case WM_SIZE: if (!stage_win32->is_foreign_win /* Ignore size changes resulting from the stage being minimized - otherwise the window size will be set to 0,0 */ && msg->wParam != SIZE_MINIMIZED) { WORD new_width = LOWORD (msg->lParam); WORD new_height = HIWORD (msg->lParam); gfloat old_width, old_height; clutter_actor_get_size (CLUTTER_ACTOR (stage), &old_width, &old_height); if (new_width != old_width || new_height != old_height) clutter_actor_set_size (CLUTTER_ACTOR (stage), new_width, new_height); } break; case WM_SHOWWINDOW: if (msg->wParam) clutter_stage_win32_map (stage_win32); else clutter_stage_win32_unmap (stage_win32); break; case WM_ACTIVATE: if (msg->wParam == WA_INACTIVE) { if (stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED) { ClutterEvent *event = clutter_event_new (CLUTTER_STAGE_STATE); stage_win32->state &= ~CLUTTER_STAGE_STATE_ACTIVATED; event->any.stage = stage; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_win32->state; take_and_queue_event (event); } } else if (!(stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED)) { ClutterEvent *event = clutter_event_new (CLUTTER_STAGE_STATE); stage_win32->state |= CLUTTER_STAGE_STATE_ACTIVATED; event->any.stage = stage; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_win32->state; take_and_queue_event (event); } break; case WM_PAINT: CLUTTER_NOTE (MULTISTAGE, "expose for stage:%p, redrawing", stage); clutter_redraw (stage); break; case WM_DESTROY: { ClutterEvent *event = clutter_event_new (CLUTTER_DESTROY_NOTIFY); CLUTTER_NOTE (EVENT, "WM_DESTROY"); event->any.stage = stage; take_and_queue_event (event); } break; case WM_CLOSE: { ClutterEvent *event = clutter_event_new (CLUTTER_DELETE); CLUTTER_NOTE (EVENT, "WM_CLOSE"); event->any.stage = stage; take_and_queue_event (event); /* The default window proc will destroy the window so we want to prevent this to allow applications to optionally destroy the window themselves */ return_value = TRUE; } break; case WM_LBUTTONDOWN: make_button_event (msg, stage, 1, 1, FALSE, core_pointer); break; case WM_MBUTTONDOWN: make_button_event (msg, stage, 2, 1, FALSE, core_pointer); break; case WM_RBUTTONDOWN: make_button_event (msg, stage, 3, 1, FALSE, core_pointer); break; case WM_LBUTTONUP: make_button_event (msg, stage, 1, 1, TRUE, core_pointer); break; case WM_MBUTTONUP: make_button_event (msg, stage, 2, 1, TRUE, core_pointer); break; case WM_RBUTTONUP: make_button_event (msg, stage, 3, 1, TRUE, core_pointer); break; case WM_LBUTTONDBLCLK: make_button_event (msg, stage, 1, 2, FALSE, core_pointer); break; case WM_MBUTTONDBLCLK: make_button_event (msg, stage, 2, 2, FALSE, core_pointer); break; case WM_RBUTTONDBLCLK: make_button_event (msg, stage, 3, 2, FALSE, core_pointer); break; case WM_MOUSEWHEEL: stage_win32->scroll_pos += (SHORT) HIWORD (msg->wParam); while (abs (stage_win32->scroll_pos) >= WHEEL_DELTA) { ClutterEvent *event = clutter_event_new (CLUTTER_SCROLL); POINT pt; event->scroll.time = msg->time; event->scroll.modifier_state = get_modifier_state (LOWORD (msg->wParam)); event->any.stage = stage; clutter_event_set_device (event, core_pointer); /* conversion to window coordinates is required */ pt.x = GET_X_LPARAM (msg->lParam); pt.y = GET_Y_LPARAM (msg->lParam); ScreenToClient (msg->hwnd, &pt); event->scroll.x = pt.x; event->scroll.y = pt.y; if (stage_win32->scroll_pos > 0) { event->scroll.direction = CLUTTER_SCROLL_UP; stage_win32->scroll_pos -= WHEEL_DELTA; } else { event->scroll.direction = CLUTTER_SCROLL_DOWN; stage_win32->scroll_pos += WHEEL_DELTA; } take_and_queue_event (event); } break; case WM_MOUSEMOVE: { ClutterEvent *event = clutter_event_new (CLUTTER_MOTION); event->motion.time = msg->time; event->motion.x = GET_X_LPARAM (msg->lParam); event->motion.y = GET_Y_LPARAM (msg->lParam); event->motion.modifier_state = get_modifier_state (msg->wParam); event->any.stage = stage; clutter_event_set_device (event, core_pointer); /* We need to start tracking when the mouse enters the stage if we're not already */ if (!stage_win32->tracking_mouse) { ClutterEvent *crossing = clutter_event_new (CLUTTER_ENTER); TRACKMOUSEEVENT tmevent; tmevent.cbSize = sizeof (tmevent); tmevent.dwFlags = TME_LEAVE; tmevent.hwndTrack = stage_win32->hwnd; TrackMouseEvent (&tmevent); event->crossing.time = msg->time; event->crossing.x = event->motion.x; event->crossing.y = event->motion.y; event->crossing.stage = stage; event->crossing.source = CLUTTER_ACTOR (stage); event->crossing.related = NULL; clutter_event_set_device (event, core_pointer); /* we entered the stage */ _clutter_stage_add_device (stage, event->crossing.device); take_and_queue_event (crossing); stage_win32->tracking_mouse = TRUE; } take_and_queue_event (event); } break; case WM_MOUSELEAVE: { ClutterEvent *event = clutter_event_new (CLUTTER_LEAVE); event->crossing.time = msg->time; event->crossing.x = msg->pt.x; event->crossing.y = msg->pt.y; event->crossing.stage = stage; event->crossing.source = CLUTTER_ACTOR (stage); event->crossing.related = NULL; clutter_event_set_device (event, core_pointer); /* we left the stage */ _clutter_stage_remove_device (stage, core_pointer); /* When we get a leave message the mouse tracking is automatically cancelled so we'll need to start it again when the mouse next enters the window */ stage_win32->tracking_mouse = FALSE; take_and_queue_event (event); } break; case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP: { ClutterEvent *event = clutter_event_new (CLUTTER_EVENT_NONE); int scan_code = (msg->lParam >> 16) & 0xff; int min = 0, max = CLUTTER_WIN32_KEY_MAP_SIZE, mid; BYTE key_states[256]; /* Get the keyboard modifier states. GetKeyboardState conveniently gets the key state that was current when the last keyboard message read was generated */ GetKeyboardState(key_states); /* Binary chop to check if we have a direct mapping for this key code */ while (min < max) { mid = (min + max) / 2; if (clutter_win32_key_map[mid].win_sym == msg->wParam) { event->key.keyval = clutter_win32_key_map[mid].clutter_sym; event->key.unicode_value = 0; break; } else if (clutter_win32_key_map[mid].win_sym < msg->wParam) min = mid + 1; else max = mid; } /* If we don't have a direct mapping then try getting the unicode value of the key sym */ if (min >= max) { WCHAR ch; BYTE shift_state[256]; /* Translate to a Unicode value, but only take into account the shift key. That way Ctrl+Shift+C will generate a capital C virtual key code with a zero unicode value for example */ memset (shift_state, 0, 256); shift_state[VK_SHIFT] = key_states[VK_SHIFT]; shift_state[VK_LSHIFT] = key_states[VK_LSHIFT]; shift_state[VK_RSHIFT] = key_states[VK_RSHIFT]; shift_state[VK_CAPITAL] = key_states[VK_CAPITAL]; if (ToUnicode (msg->wParam, scan_code, shift_state, &ch, 1, 0) == 1 /* The codes in this range directly match the Latin 1 codes so we can just use the Unicode value as the key sym */ && ch >= 0x20 && ch <= 0xff) event->key.keyval = ch; else /* Otherwise we don't know what the key means but the application might be able to do something with the scan code so we might as well still generate the event */ event->key.keyval = CLUTTER_KEY_VoidSymbol; /* Get the unicode value of the keypress again using the full modifier state */ if (ToUnicode (msg->wParam, scan_code, key_states, &ch, 1, 0) == 1) event->key.unicode_value = ch; else event->key.unicode_value = 0; } event->key.type = msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE; event->key.time = msg->time; event->key.modifier_state = get_key_modifier_state (key_states); event->key.hardware_keycode = scan_code; event->any.stage = stage; clutter_event_set_device (event, core_keyboard); take_and_queue_event (event); } break; case WM_GETMINMAXINFO: { MINMAXINFO *min_max_info = (MINMAXINFO *) msg->lParam; _clutter_stage_win32_get_min_max_info (stage_win32, min_max_info); return_value = TRUE; } break; case WM_SETCURSOR: /* If the cursor is in the window's client area and the stage's cursor should be invisible then we'll set a blank cursor instead */ if (LOWORD (msg->lParam) == HTCLIENT && !stage_win32->is_cursor_visible) { return_value = TRUE; _clutter_stage_win32_update_cursor (stage_win32); } break; } return return_value; }
static gboolean message_translate (ClutterBackend *backend, ClutterEvent *event, const MSG *msg, gboolean *call_def_window_proc) { ClutterBackendWin32 *backend_win32; ClutterStageWin32 *stage_win32; ClutterStage *stage; ClutterStageWindow *impl; gboolean res; backend_win32 = CLUTTER_BACKEND_WIN32 (backend); /* Do further processing only on events for the stage window */ stage = clutter_win32_get_stage_from_window (msg->hwnd); if (stage == NULL) return FALSE; impl = _clutter_stage_get_window (stage); stage_win32 = CLUTTER_STAGE_WIN32 (impl); event->any.stage = stage; res = TRUE; switch (msg->message) { case WM_SIZE: if (!stage_win32->is_foreign_win /* Ignore size changes resulting from the stage being minimized - otherwise the window size will be set to 0,0 */ && msg->wParam != SIZE_MINIMIZED) { WORD new_width = LOWORD (msg->lParam); WORD new_height = HIWORD (msg->lParam); guint old_width, old_height; clutter_actor_get_size (CLUTTER_ACTOR (stage), &old_width, &old_height); if (new_width != old_width || new_height != old_height) clutter_actor_set_size (CLUTTER_ACTOR (stage), new_width, new_height); } res = FALSE; break; case WM_SHOWWINDOW: if (msg->wParam) clutter_stage_win32_map (stage_win32); else clutter_stage_win32_unmap (stage_win32); res = FALSE; break; case WM_ACTIVATE: if (msg->wParam == WA_INACTIVE) { if (stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED) { stage_win32->state &= ~CLUTTER_STAGE_STATE_ACTIVATED; event->type = CLUTTER_STAGE_STATE; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_win32->state; } else res = FALSE; break; } else { if (!(stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED)) { stage_win32->state |= CLUTTER_STAGE_STATE_ACTIVATED; event->type = CLUTTER_STAGE_STATE; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_win32->state; } else res = FALSE; } break; case WM_PAINT: CLUTTER_NOTE (MULTISTAGE, "expose for stage:%p, redrawing", stage); clutter_redraw (stage); res = FALSE; break; case WM_DESTROY: CLUTTER_NOTE (EVENT, "WM_DESTROY"); event->type = CLUTTER_DESTROY_NOTIFY; break; case WM_CLOSE: CLUTTER_NOTE (EVENT, "WM_CLOSE"); event->type = CLUTTER_DELETE; /* The default window proc will destroy the window so we want to prevent this to allow applications to optionally destroy the window themselves */ if (call_def_window_proc) *call_def_window_proc = FALSE; break; case WM_LBUTTONDOWN: make_button_event (msg, event, 1, 1, FALSE); break; case WM_MBUTTONDOWN: make_button_event (msg, event, 2, 1, FALSE); break; case WM_RBUTTONDOWN: make_button_event (msg, event, 3, 1, FALSE); break; case WM_LBUTTONUP: make_button_event (msg, event, 1, 1, TRUE); break; case WM_MBUTTONUP: make_button_event (msg, event, 2, 1, TRUE); break; case WM_RBUTTONUP: make_button_event (msg, event, 3, 1, TRUE); break; case WM_LBUTTONDBLCLK: make_button_event (msg, event, 1, 2, FALSE); break; case WM_MBUTTONDBLCLK: make_button_event (msg, event, 2, 2, FALSE); break; case WM_RBUTTONDBLCLK: make_button_event (msg, event, 3, 2, FALSE); break; case WM_MOUSEWHEEL: stage_win32->scroll_pos += (SHORT) HIWORD (msg->wParam); event->type = CLUTTER_SCROLL; event->scroll.time = msg->time; event->scroll.modifier_state = get_modifier_state (LOWORD (msg->wParam)); /* conversion to window coordinates is required */ { POINT pt = { GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam) }; ScreenToClient (msg->hwnd, &pt); event->scroll.x = pt.x; event->scroll.y = pt.y; } if (stage_win32->scroll_pos >= WHEEL_DELTA) { event->scroll.direction = CLUTTER_SCROLL_UP; stage_win32->scroll_pos -= WHEEL_DELTA; } else if (stage_win32->scroll_pos <= -WHEEL_DELTA) { event->scroll.direction = CLUTTER_SCROLL_DOWN; stage_win32->scroll_pos += WHEEL_DELTA; } else res = FALSE; break; case WM_MOUSEMOVE: event->type = CLUTTER_MOTION; event->motion.time = msg->time; event->motion.x = GET_X_LPARAM (msg->lParam); event->motion.y = GET_Y_LPARAM (msg->lParam); event->motion.modifier_state = get_modifier_state (msg->wParam); break; case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP: { int scan_code = (msg->lParam >> 16) & 0xff; int min = 0, max = CLUTTER_WIN32_KEY_MAP_SIZE, mid; BYTE key_states[256]; /* Get the keyboard modifier states. GetKeyboardState conveniently gets the key state that was current when the last keyboard message read was generated */ GetKeyboardState(key_states); /* Binary chop to check if we have a direct mapping for this key code */ while (min < max) { mid = (min + max) / 2; if (clutter_win32_key_map[mid].win_sym == msg->wParam) { event->key.keyval = clutter_win32_key_map[mid].clutter_sym; event->key.unicode_value = 0; break; } else if (clutter_win32_key_map[mid].win_sym < msg->wParam) min = mid + 1; else max = mid; } /* If we don't have a direct mapping then try getting the unicode value of the key sym */ if (min >= max) { WCHAR ch; BYTE shift_state[256]; /* Translate to a Unicode value, but only take into account the shift key. That way Ctrl+Shift+C will generate a capital C virtual key code with a zero unicode value for example */ memset (shift_state, 0, 256); shift_state[VK_SHIFT] = key_states[VK_SHIFT]; shift_state[VK_LSHIFT] = key_states[VK_LSHIFT]; shift_state[VK_RSHIFT] = key_states[VK_RSHIFT]; shift_state[VK_CAPITAL] = key_states[VK_CAPITAL]; if (ToUnicode (msg->wParam, scan_code, shift_state, &ch, 1, 0) == 1 /* The codes in this range directly match the Latin 1 codes so we can just use the Unicode value as the key sym */ && ch >= 0x20 && ch <= 0xff) event->key.keyval = ch; else /* Otherwise we don't know what the key means but the application might be able to do something with the scan code so we might as well still generate the event */ event->key.keyval = CLUTTER_VoidSymbol; /* Get the unicode value of the keypress again using the full modifier state */ if (ToUnicode (msg->wParam, scan_code, key_states, &ch, 1, 0) == 1) event->key.unicode_value = ch; else event->key.unicode_value = 0; } event->key.type = msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE; event->key.time = msg->time; event->key.modifier_state = get_key_modifier_state (key_states); event->key.hardware_keycode = scan_code; } break; case WM_GETMINMAXINFO: { MINMAXINFO *min_max_info = (MINMAXINFO *) msg->lParam; _clutter_stage_win32_get_min_max_info (stage_win32, min_max_info); if (call_def_window_proc) *call_def_window_proc = FALSE; } break; default: /* ignore every other message */ res = FALSE; break; } return res; }
static void clutter_backend_egl_ensure_context (ClutterBackend *backend, ClutterStage *stage) { ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend); ClutterStageWindow *impl; if (stage == NULL || (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_IN_DESTRUCTION) || ((impl = _clutter_stage_get_window (stage)) == NULL)) { CLUTTER_NOTE (BACKEND, "Clearing EGL context"); eglMakeCurrent (backend_egl->edpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } else { ClutterStageEGL *stage_egl; ClutterStageX11 *stage_x11; g_assert (impl != NULL); CLUTTER_NOTE (MULTISTAGE, "Setting context for stage of type %s [%p]", g_type_name (G_OBJECT_TYPE (impl)), impl); stage_egl = CLUTTER_STAGE_EGL (impl); stage_x11 = CLUTTER_STAGE_X11 (impl); if (backend_egl->egl_context == EGL_NO_CONTEXT) return; clutter_x11_trap_x_errors (); /* we might get here inside the final dispose cycle, so we * need to handle this gracefully */ if (stage_x11->xwin == None || stage_egl->egl_surface == EGL_NO_SURFACE) { CLUTTER_NOTE (MULTISTAGE, "Received a stale stage, clearing all context"); if (backend_egl->dummy_surface == EGL_NO_SURFACE) eglMakeCurrent (backend_egl->edpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); else eglMakeCurrent (backend_egl->edpy, backend_egl->dummy_surface, backend_egl->dummy_surface, backend_egl->egl_context); } else { CLUTTER_NOTE (MULTISTAGE, "Setting real surface current"); eglMakeCurrent (backend_egl->edpy, stage_egl->egl_surface, stage_egl->egl_surface, backend_egl->egl_context); } if (clutter_x11_untrap_x_errors ()) g_critical ("Unable to make the stage window 0x%x the current " "EGLX drawable", (int) stage_x11->xwin); } }