/** * clutter_x11_texture_pixmap_set_window: * @texture: the texture to bind * @window: the X window to which the texture should be bound * @automatic: TRUE is automatic window updates, FALSE for manual. * * Sets up a suitable pixmap for the window, using the composite and damage * extensions if possible, and then calls * clutter_x11_texture_pixmap_set_pixmap(). If you want a window in a texture, * you probably want this function, or its older sister, * clutter_glx_texture_pixmap_set_window(). * * Since: 0.8 **/ void clutter_x11_texture_pixmap_set_window (ClutterX11TexturePixmap *texture, Window window) { ClutterX11TexturePixmapPrivate *priv; XWindowAttributes attr; Display *dpy = clutter_x11_get_default_display (); g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture)); priv = texture->priv; if (!clutter_x11_has_composite_extension()) return; if (priv->window == window) return; if (priv->window) { clutter_x11_remove_filter (on_x_event_filter_too, (gpointer)texture); } priv->window = window; priv->window_mapped = FALSE; priv->destroyed = FALSE; if (window == None) return; clutter_x11_trap_x_errors (); { if (!XGetWindowAttributes (dpy, window, &attr)) { XSync (dpy, False); clutter_x11_untrap_x_errors (); g_warning ("bad window 0x%x", (guint32)window); priv->window = None; return; } } clutter_x11_untrap_x_errors (); if (priv->window) { XSelectInput (dpy, priv->window, attr.your_event_mask | StructureNotifyMask); clutter_x11_add_filter (on_x_event_filter_too, (gpointer)texture); } g_object_ref (texture); g_object_notify (G_OBJECT (texture), "window"); clutter_x11_texture_pixmap_set_mapped (texture, attr.map_state == IsViewable); clutter_x11_texture_pixmap_sync_window (texture); g_object_unref (texture); }
static void clutter_glx_texture_pixmap_unrealize (ClutterActor *actor) { ClutterGLXTexturePixmapPrivate *priv; Display *dpy; priv = CLUTTER_GLX_TEXTURE_PIXMAP (actor)->priv; dpy = clutter_x11_get_default_display(); if (!_have_tex_from_pixmap_ext) { CLUTTER_ACTOR_CLASS (clutter_glx_texture_pixmap_parent_class)-> unrealize (actor); return; } if (!CLUTTER_ACTOR_IS_REALIZED (actor)) return; if (priv->glx_pixmap && priv->bound) { clutter_x11_trap_x_errors (); (_gl_release_tex_image) (dpy, priv->glx_pixmap, GLX_FRONT_LEFT_EXT); XSync (clutter_x11_get_default_display(), FALSE); clutter_x11_untrap_x_errors (); priv->bound = FALSE; } CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); }
/** * st_clipboard_set_text: * @clipboard: A #StClipboard * @type: The type of clipboard that you want to set * @text: text to copy to the clipboard * * Sets text as the current contents of the clipboard. */ void st_clipboard_set_text (StClipboard *clipboard, StClipboardType type, const gchar *text) { StClipboardPrivate *priv; Display *dpy; g_return_if_fail (ST_IS_CLIPBOARD (clipboard)); g_return_if_fail (text != NULL); priv = clipboard->priv; /* make a copy of the text */ g_free (priv->clipboard_text); priv->clipboard_text = g_strdup (text); /* tell X we own the clipboard selection */ dpy = clutter_x11_get_default_display (); clutter_x11_trap_x_errors (); XSetSelectionOwner (dpy, type == ST_CLIPBOARD_TYPE_CLIPBOARD ? __atom_clip : __atom_primary, priv->clipboard_window, CurrentTime); XSync (dpy, FALSE); clutter_x11_untrap_x_errors (); }
static void clutter_stage_glx_unrealize (ClutterStageWindow *stage_window) { ClutterBackend *backend = clutter_get_default_backend (); ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window); /* Note unrealize should free up any backend stage related resources */ CLUTTER_NOTE (BACKEND, "Unrealizing stage"); clutter_x11_trap_x_errors (); if (stage_glx->glxwin != None) { glXDestroyWindow (backend_x11->xdpy, stage_glx->glxwin); stage_glx->glxwin = None; } if (!stage_x11->is_foreign_xwin && stage_x11->xwin != None) { XDestroyWindow (backend_x11->xdpy, stage_x11->xwin); stage_x11->xwin = None; } else stage_x11->xwin = None; XSync (backend_x11->xdpy, False); clutter_x11_untrap_x_errors (); CLUTTER_MARK (); }
static gboolean xembed_send_message (ClutterBackendX11 *backend_x11, Window window, long message, long detail, long data1, long data2) { XEvent ev; memset (&ev, 0, sizeof (ev)); ev.xclient.type = ClientMessage; ev.xclient.window = window; ev.xclient.message_type = backend_x11->atom_XEMBED; ev.xclient.format = 32; ev.xclient.data.l[0] = CurrentTime; ev.xclient.data.l[1] = message; ev.xclient.data.l[2] = detail; ev.xclient.data.l[3] = data1; ev.xclient.data.l[4] = data2; clutter_x11_trap_x_errors (); XSendEvent (backend_x11->xdpy, window, False, NoEventMask, &ev); XSync (backend_x11->xdpy, False); if (clutter_x11_untrap_x_errors ()) return False; return True; }
static ClutterX11FilterReturn st_clipboard_provider (XEvent *xev, ClutterEvent *cev, StClipboard *clipboard) { XSelectionEvent notify_event; XSelectionRequestEvent *req_event; if (xev->type != SelectionRequest) return CLUTTER_X11_FILTER_CONTINUE; req_event = &xev->xselectionrequest; clutter_x11_trap_x_errors (); if (req_event->target == __atom_targets) { XChangeProperty (req_event->display, req_event->requestor, req_event->property, XA_ATOM, 32, PropModeReplace, (guchar*) clipboard->priv->supported_targets, clipboard->priv->n_targets); } else { XChangeProperty (req_event->display, req_event->requestor, req_event->property, req_event->target, 8, PropModeReplace, (guchar*) clipboard->priv->clipboard_text, strlen (clipboard->priv->clipboard_text)); } notify_event.type = SelectionNotify; notify_event.display = req_event->display; notify_event.requestor = req_event->requestor; notify_event.selection = req_event->selection; notify_event.target = req_event->target; notify_event.time = req_event->time; if (req_event->property == None) notify_event.property = req_event->target; else notify_event.property = req_event->property; /* notify the requestor that they have a copy of the selection */ XSendEvent (req_event->display, req_event->requestor, False, 0, (XEvent *) ¬ify_event); /* Make it happen non async */ XSync (clutter_x11_get_default_display(), FALSE); clutter_x11_untrap_x_errors (); /* FIXME: Warn here on fail ? */ return CLUTTER_X11_FILTER_REMOVE; }
/** * st_clipboard_get_text: * @clipboard: A #StCliboard * @type: The type of clipboard data you want * @callback: (scope async): function to be called when the text is retreived * @user_data: data to be passed to the callback * * Request the data from the clipboard in text form. @callback is executed * when the data is retreived. * */ void st_clipboard_get_text (StClipboard *clipboard, StClipboardType type, StClipboardCallbackFunc callback, gpointer user_data) { EventFilterData *data; Display *dpy; g_return_if_fail (ST_IS_CLIPBOARD (clipboard)); g_return_if_fail (callback != NULL); data = g_new0 (EventFilterData, 1); data->clipboard = clipboard; data->callback = callback; data->user_data = user_data; clutter_x11_add_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter, data); dpy = clutter_x11_get_default_display (); clutter_x11_trap_x_errors (); /* safety on */ XConvertSelection (dpy, type == ST_CLIPBOARD_TYPE_CLIPBOARD ? __atom_clip : __atom_primary, __utf8_string, __utf8_string, clipboard->priv->clipboard_window, CurrentTime); clutter_x11_untrap_x_errors (); }
static ClutterX11FilterReturn st_clipboard_x11_event_filter (XEvent *xev, ClutterEvent *cev, EventFilterData *filter_data) { Atom actual_type; int actual_format, result; unsigned long nitems, bytes_after; unsigned char *data = NULL; if(xev->type != SelectionNotify) return CLUTTER_X11_FILTER_CONTINUE; if (xev->xselection.property == None) { /* clipboard empty */ filter_data->callback (filter_data->clipboard, NULL, filter_data->user_data); clutter_x11_remove_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter, filter_data); g_free (filter_data); return CLUTTER_X11_FILTER_REMOVE; } clutter_x11_trap_x_errors (); result = XGetWindowProperty (xev->xselection.display, xev->xselection.requestor, xev->xselection.property, 0L, G_MAXINT, True, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &data); if (clutter_x11_untrap_x_errors () || result != Success) { /* FIXME: handle failure better */ g_warning ("Clipboard: prop retrival failed"); } filter_data->callback (filter_data->clipboard, (char*) data, filter_data->user_data); clutter_x11_remove_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter, filter_data); g_free (filter_data); if (data) XFree (data); return CLUTTER_X11_FILTER_REMOVE; }
static void clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture, gint x, gint y, gint width, gint height) { ClutterGLXTexturePixmapPrivate *priv; Display *dpy; CLUTTER_NOTE (TEXTURE, "Updating texture pixmap"); priv = CLUTTER_GLX_TEXTURE_PIXMAP (texture)->priv; dpy = clutter_x11_get_default_display(); if (!CLUTTER_ACTOR_IS_REALIZED (texture)) return; if (priv->use_fallback) { CLUTTER_NOTE (TEXTURE, "Falling back to X11"); parent_class->update_area (texture, x, y, width, height); return; } if (priv->glx_pixmap == None) return; if (texture_bind (CLUTTER_GLX_TEXTURE_PIXMAP(texture))) { CLUTTER_NOTE (TEXTURE, "Really updating via GLX"); clutter_x11_trap_x_errors (); (_gl_bind_tex_image) (dpy, priv->glx_pixmap, GLX_FRONT_LEFT_EXT, NULL); XSync (clutter_x11_get_default_display(), FALSE); /* Note above fires X error for non name pixmaps - but * things still seem to work - i.e pixmap updated */ if (clutter_x11_untrap_x_errors ()) CLUTTER_NOTE (TEXTURE, "Update bind_tex_image failed"); priv->bound = TRUE; } else g_warning ("Failed to bind initial tex"); if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR(texture))) clutter_actor_queue_redraw (CLUTTER_ACTOR(texture)); }
static ClutterX11FilterReturn on_x_event_filter (XEvent *xev, ClutterEvent *cev, gpointer data) { ClutterX11TexturePixmap *texture; ClutterX11TexturePixmapPrivate *priv; Display *dpy; texture = CLUTTER_X11_TEXTURE_PIXMAP (data); g_return_val_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture), \ CLUTTER_X11_FILTER_CONTINUE); dpy = clutter_x11_get_default_display(); priv = texture->priv; if (xev->type == _damage_event_base + XDamageNotify) { XserverRegion parts; gint i, r_count; XRectangle *r_damage; XRectangle r_bounds; XDamageNotifyEvent *dev = (XDamageNotifyEvent*)xev; if (dev->drawable != priv->damage_drawable) return CLUTTER_X11_FILTER_CONTINUE; clutter_x11_trap_x_errors (); /* * Retrieve the damaged region and break it down into individual * rectangles so we do not have to update the whole shebang. */ parts = XFixesCreateRegion (dpy, 0, 0); XDamageSubtract (dpy, priv->damage, None, parts); r_damage = XFixesFetchRegionAndBounds (dpy, parts, &r_count, &r_bounds); clutter_x11_untrap_x_errors (); if (r_damage) { for (i = 0; i < r_count; ++i) clutter_x11_texture_pixmap_update_area (texture, r_damage[i].x, r_damage[i].y, r_damage[i].width, r_damage[i].height); XFree (r_damage); } XFixesDestroyRegion (dpy, parts); } return CLUTTER_X11_FILTER_CONTINUE; }
static void clutter_glx_texture_pixmap_free_glx_pixmap (ClutterGLXTexturePixmap *texture) { ClutterGLXTexturePixmapPrivate *priv = texture->priv; Display *dpy; dpy = clutter_x11_get_default_display (); if (priv->glx_pixmap && priv->bound) { texture_bind (texture); clutter_x11_trap_x_errors (); (_gl_release_tex_image) (dpy, priv->glx_pixmap, GLX_FRONT_LEFT_EXT); XSync (clutter_x11_get_default_display(), FALSE); if (clutter_x11_untrap_x_errors ()) CLUTTER_NOTE (TEXTURE, "Failed to release?"); CLUTTER_NOTE (TEXTURE, "Destroyed pxm: %li", priv->glx_pixmap); priv->bound = FALSE; } clutter_x11_trap_x_errors (); if (priv->glx_pixmap) glXDestroyGLXPixmap (dpy, priv->glx_pixmap); XSync (dpy, FALSE); clutter_x11_untrap_x_errors (); priv->glx_pixmap = None; }
static void free_damage_resources (ClutterX11TexturePixmap *texture) { ClutterX11TexturePixmapPrivate *priv; Display *dpy; priv = texture->priv; dpy = clutter_x11_get_default_display(); if (priv->damage) { clutter_x11_trap_x_errors (); XDamageDestroy (dpy, priv->damage); XSync (dpy, FALSE); clutter_x11_untrap_x_errors (); priv->damage = None; priv->damage_drawable = None; } clutter_x11_remove_filter (on_x_event_filter, (gpointer)texture); }
void clutter_x11_texture_pixmap_set_automatic (ClutterX11TexturePixmap *texture, gboolean setting) { ClutterX11TexturePixmapPrivate *priv; Display *dpy; g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture)); priv = texture->priv; if (setting == priv->automatic_updates) return; dpy = clutter_x11_get_default_display(); if (setting == TRUE) { clutter_x11_add_filter (on_x_event_filter, (gpointer)texture); clutter_x11_trap_x_errors (); if (priv->window) priv->damage_drawable = priv->window; else priv->damage_drawable = priv->pixmap; priv->damage = XDamageCreate (dpy, priv->damage_drawable, XDamageReportNonEmpty); XSync (dpy, FALSE); clutter_x11_untrap_x_errors (); } else free_damage_resources (texture); priv->automatic_updates = setting; }
static void clutter_glx_texture_pixmap_dispose (GObject *object) { ClutterGLXTexturePixmapPrivate *priv; priv = CLUTTER_GLX_TEXTURE_PIXMAP (object)->priv; if (priv->glx_pixmap != None) { clutter_x11_trap_x_errors (); glXDestroyGLXPixmap (clutter_x11_get_default_display(), priv->glx_pixmap); XSync (clutter_x11_get_default_display(), FALSE); clutter_x11_untrap_x_errors (); priv->glx_pixmap = None; } G_OBJECT_CLASS (clutter_glx_texture_pixmap_parent_class)->dispose (object); }
static void clutter_stage_glx_unrealize (ClutterStageWindow *stage_window) { ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (stage_window); ClutterBackendX11 *backend_x11 = stage_x11->backend; /* Note unrealize should free up any backend stage related resources */ CLUTTER_NOTE (BACKEND, "Unrealizing GLX stage [%p]", stage_glx); clutter_x11_trap_x_errors (); if (stage_glx->glxwin != None) { glXDestroyWindow (backend_x11->xdpy, stage_glx->glxwin); stage_glx->glxwin = None; } _clutter_stage_x11_destroy_window_untrapped (stage_x11); XSync (backend_x11->xdpy, False); clutter_x11_untrap_x_errors (); }
/** * 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; }
/* Tries to allocate enough shared mem to handle a full size * update size of the X Pixmap. */ static gboolean try_alloc_shm (ClutterX11TexturePixmap *texture) { ClutterX11TexturePixmapPrivate *priv; XImage *dummy_image; Display *dpy; priv = texture->priv; dpy = clutter_x11_get_default_display(); g_return_val_if_fail (priv->pixmap, FALSE); if (!XShmQueryExtension(dpy) || g_getenv("CLUTTER_X11_NO_SHM")) { priv->have_shm = FALSE; return FALSE; } clutter_x11_trap_x_errors (); /* We are creating a dummy_image so we can have Xlib calculate * image->bytes_per_line - including any magic padding it may * want - for the largest possible ximage we might need to use * when handling updates to the texture. * * Note: we pass a NULL shminfo here, but that has no bearing * on the setup of the XImage, except that ximage->obdata will * == NULL. */ dummy_image = XShmCreateImage(dpy, DefaultVisual(dpy, clutter_x11_get_default_screen()), priv->depth, ZPixmap, NULL, NULL, /* shminfo, */ priv->pixmap_width, priv->pixmap_height); if (!dummy_image) goto failed_image_create; priv->shminfo.shmid = shmget (IPC_PRIVATE, dummy_image->bytes_per_line * dummy_image->height, IPC_CREAT|0777); if (priv->shminfo.shmid == -1) goto failed_shmget; priv->shminfo.shmaddr = shmat (priv->shminfo.shmid, 0, 0); if (priv->shminfo.shmaddr == (void *)-1) goto failed_shmat; priv->shminfo.readOnly = False; if (XShmAttach(dpy, &priv->shminfo) == 0) goto failed_xshmattach; if (clutter_x11_untrap_x_errors ()) g_warning ("X Error: Failed to setup XShm"); priv->have_shm = TRUE; if (dummy_image) XFree (dummy_image); return TRUE; failed_xshmattach: g_warning ("XShmAttach failed"); shmdt(priv->shminfo.shmaddr); failed_shmat: g_warning ("shmat failed"); shmctl(priv->shminfo.shmid, IPC_RMID, 0); failed_shmget: g_warning ("shmget failed"); XDestroyImage(dummy_image); failed_image_create: if (clutter_x11_untrap_x_errors ()) g_warning ("X Error: Failed to setup XShm"); priv->have_shm = FALSE; return FALSE; }
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; }
static void clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture, gint x, gint y, gint width, gint height) { ClutterX11TexturePixmapPrivate *priv; Display *dpy; XImage *image; char *first_pixel; GError *error = NULL; guint bytes_per_line; char *data; int err_code; char pixel_bpp; gboolean pixel_has_alpha; #if 0 clock_t start_t = clock(); #endif if (!CLUTTER_ACTOR_IS_REALIZED (texture)) return; priv = texture->priv; dpy = clutter_x11_get_default_display(); if (!priv->pixmap) return; if (priv->shminfo.shmid == -1) try_alloc_shm (texture); clutter_x11_trap_x_errors (); if (priv->have_shm) { image = XShmCreateImage(dpy, DefaultVisual(dpy, clutter_x11_get_default_screen()), priv->depth, ZPixmap, NULL, &priv->shminfo, width, height); image->data = priv->shminfo.shmaddr; XShmGetImage (dpy, priv->pixmap, image, x, y, AllPlanes); first_pixel = image->data; } else { if (!priv->image) { priv->image = XGetImage (dpy, priv->pixmap, 0, 0, priv->pixmap_width, priv->pixmap_height, AllPlanes, ZPixmap); if (priv->image) first_pixel = priv->image->data + priv->image->bytes_per_line * y + x * priv->image->bits_per_pixel/8; else { g_warning ("%s: XGetImage() failed", __FUNCTION__); return; } } else { XGetSubImage (dpy, priv->pixmap, x, y, width, height, AllPlanes, ZPixmap, priv->image, x, y); first_pixel = priv->image->data + priv->image->bytes_per_line * y + x * priv->image->bits_per_pixel/8; } image = priv->image; } XSync (dpy, FALSE); if ((err_code = clutter_x11_untrap_x_errors ())) { g_warning ("Failed to get XImage of pixmap: %lx, removing", priv->pixmap); /* safe to assume pixmap has gone away? - therefor reset */ clutter_x11_texture_pixmap_set_pixmap (texture, None); goto free_image_and_return; } if (priv->depth == 24) { bytes_per_line = image->bytes_per_line; data = first_pixel; pixel_bpp = 3; pixel_has_alpha = FALSE; } else if (priv->depth == 16) { bytes_per_line = image->bytes_per_line; data = first_pixel; pixel_bpp = 2; pixel_has_alpha = FALSE; } else if (priv->depth == 32) { bytes_per_line = image->bytes_per_line; data = first_pixel; pixel_bpp = 4; pixel_has_alpha = TRUE; } else goto free_image_and_return; if (!priv->allow_alpha) pixel_has_alpha = FALSE; /* For debugging purposes, un comment to simply generate dummy * pixmap data. (A Green background and Blue cross) */ #if 0 { guint xpos, ypos; if (data_allocated) g_free (data); data_allocated = TRUE; data = g_malloc (width*height*4); bytes_per_line = width *4; for (ypos=0; ypos<height; ypos++) for (xpos=0; xpos<width; xpos++) { char *p = data + width*4*ypos + xpos * 4; guint32 *pixel = (guint32 *)p; if ((xpos > width/2 && xpos <= (width/2) + width/4) || (ypos > height/2 && ypos <= (height/2) + height/4)) *pixel=0xff0000ff; else *pixel=0xff00ff00; } } #endif if (x != 0 || y != 0 || width != priv->pixmap_width || height != priv->pixmap_height) clutter_texture_set_area_from_rgb_data (CLUTTER_TEXTURE (texture), (guint8 *)data, pixel_has_alpha, x, y, width, height, bytes_per_line, pixel_bpp, CLUTTER_TEXTURE_RGB_FLAG_BGR, &error); else clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (texture), (guint8 *)data, pixel_has_alpha, width, height, bytes_per_line, pixel_bpp, CLUTTER_TEXTURE_RGB_FLAG_BGR, &error); if (error) { g_warning ("Error when uploading from pixbuf: %s", error->message); g_error_free (error); } free_image_and_return: if (priv->have_shm) XFree (image); #if 0 clock_t end_t = clock(); int time = (int)((double)(end_t - start_t) * (1000.0 / CLOCKS_PER_SEC)); g_print("clutter-x11-update-area-real(%d,%d,%d,%d) %d bits - %d ms\n",x,y,width,height,priv->depth,time); #endif }
/** * clutter_x11_texture_pixmap_set_pixmap: * @texture: the texture to bind * @pixmap: the X Pixmap to which the texture should be bound * * Sets the X Pixmap to which the texture should be bound. * * Since: 0.8 **/ void clutter_x11_texture_pixmap_set_pixmap (ClutterX11TexturePixmap *texture, Pixmap pixmap) { Window root; int x, y; unsigned int width, height, border_width, depth; Status status = 0; gboolean new_pixmap = FALSE, new_pixmap_width = FALSE; gboolean new_pixmap_height = FALSE, new_pixmap_depth = FALSE; ClutterX11TexturePixmapPrivate *priv; g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture)); priv = texture->priv; clutter_x11_trap_x_errors (); status = XGetGeometry (clutter_x11_get_default_display(), (Drawable)pixmap, &root, &x, &y, &width, &height, &border_width, &depth); if (clutter_x11_untrap_x_errors () || status == 0) { if (pixmap != None) g_warning ("Unable to query pixmap: %lx", pixmap); pixmap = None; width = height = depth = 0; } if (priv->image) { XDestroyImage (priv->image); priv->image = NULL; } if (priv->pixmap != pixmap) { if (priv->pixmap && priv->owns_pixmap) { g_signal_emit (texture, signals[PIXMAP_FREEING], 0, NULL); XFreePixmap (clutter_x11_get_default_display (), priv->pixmap); } priv->pixmap = pixmap; new_pixmap = TRUE; } if (priv->pixmap_width != width) { priv->pixmap_width = width; new_pixmap_width = TRUE; } if (priv->pixmap_height != height) { priv->pixmap_height = height; new_pixmap_height = TRUE; } if (priv->depth != depth) { priv->depth = depth; new_pixmap_depth = TRUE; } /* NB: We defer sending the signals until updating all the * above members so the values are all available to the * signal handlers. */ g_object_ref (texture); if (new_pixmap) g_object_notify (G_OBJECT (texture), "pixmap"); if (new_pixmap_width) g_object_notify (G_OBJECT (texture), "pixmap-width"); if (new_pixmap_height) g_object_notify (G_OBJECT (texture), "pixmap-height"); if (new_pixmap_depth) g_object_notify (G_OBJECT (texture), "pixmap-depth"); free_shm_resources (texture); if (priv->depth != 0 && priv->pixmap != None && priv->pixmap_width != 0 && priv->pixmap_height != 0) { if (CLUTTER_ACTOR_IS_REALIZED (texture)) clutter_x11_texture_pixmap_update_area (texture, 0, 0, priv->pixmap_width, priv->pixmap_height); } /* * Keep ref until here in case a notify causes removal from the scene; can't * lower the notifies because glx's notify handler needs to run before * update_area */ g_object_unref (texture); }
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); } }
gint _clutter_input_device_x11_construct (ClutterInputDevice *device, ClutterBackendX11 *backend) { int n_events = 0; #ifdef HAVE_XINPUT ClutterInputDeviceX11 *device_x11; XDevice *x_device = NULL; gint device_id; int i; device_x11 = CLUTTER_INPUT_DEVICE_X11 (device); device_id = clutter_input_device_get_device_id (device); clutter_x11_trap_x_errors (); /* retrieve the X11 device */ x_device = XOpenDevice (backend->xdpy, device_id); if (clutter_x11_untrap_x_errors () || x_device == NULL) { CLUTTER_NOTE (BACKEND, "Unable to open device %i", device_id); return 0; } device_x11->xdevice = x_device; CLUTTER_NOTE (BACKEND, "Registering XINPUT device with XID: %li", x_device->device_id); /* We must go through all the classes supported by this device and * register the appropriate events we want. Each class only appears * once. We need to store the types with the stage since they are * created dynamically by the server. They are not device specific. */ for (i = 0; i < x_device->num_classes; i++) { XInputClassInfo *xclass_info = x_device->classes + i; int *button_press, *button_release, *motion_notify; int *key_press, *key_release; button_press = &backend->event_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT]; button_release = &backend->event_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT]; motion_notify = &backend->event_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT]; key_press = &backend->event_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT]; key_release = &backend->event_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT]; switch (xclass_info->input_class) { /* event though XInput 1.x is broken for keyboard-like devices * it might still be useful to track them down; the core keyboard * will handle the right events anyway */ case KeyClass: DeviceKeyPress (x_device, *key_press, device_x11->xevent_list[n_events]); n_events++; DeviceKeyRelease (x_device, *key_release, device_x11->xevent_list[n_events]); n_events++; break; case ButtonClass: DeviceButtonPress (x_device, *button_press, device_x11->xevent_list[n_events]); n_events++; DeviceButtonRelease (x_device, *button_release, device_x11->xevent_list[n_events]); n_events++; break; case ValuatorClass: DeviceMotionNotify (x_device, *motion_notify, device_x11->xevent_list[n_events]); n_events++; break; } } device_x11->num_events = n_events; #endif /* HAVE_XINPUT */ return n_events; }
static gboolean clutter_backend_glx_create_context (ClutterBackend *backend, GError **error) { ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend); ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); GLXFBConfig config; gboolean is_direct; Window root_xwin; XSetWindowAttributes attrs; XVisualInfo *xvisinfo; Display *xdisplay; int major; int minor; GLXDrawable dummy_drawable; if (backend_glx->gl_context != None) return TRUE; xdisplay = clutter_x11_get_default_display (); root_xwin = clutter_x11_get_root_window (); if (!_clutter_backend_glx_get_fbconfig (backend_glx, &config)) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to find suitable fbconfig for the GLX context"); return FALSE; } CLUTTER_NOTE (BACKEND, "Creating GLX Context (display: %p)", xdisplay); backend_glx->gl_context = glXCreateNewContext (xdisplay, config, GLX_RGBA_TYPE, NULL, True); if (backend_glx->gl_context == None) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to create suitable GL context"); return FALSE; } is_direct = glXIsDirect (xdisplay, backend_glx->gl_context); CLUTTER_NOTE (GL, "Setting %s context", is_direct ? "direct" : "indirect"); _cogl_set_indirect_context (!is_direct); /* COGL assumes that there is always a GL context selected; in order * to make sure that a GLX context exists and is made current, we use * a dummy, offscreen override-redirect window to which we can always * fall back if no stage is available * * XXX - we need to do this dance because GLX does not allow creating * a context and querying it for basic information (even the function * pointers) unless it's made current to a real Drawable. it should be * possible to avoid this in future releases of Mesa and X11, but right * now this is the best solution available. */ xvisinfo = glXGetVisualFromFBConfig (xdisplay, config); if (xvisinfo == None) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to retrieve the X11 visual"); return FALSE; } clutter_x11_trap_x_errors (); attrs.override_redirect = True; attrs.colormap = XCreateColormap (xdisplay, root_xwin, xvisinfo->visual, AllocNone); attrs.border_pixel = 0; backend_glx->dummy_xwin = XCreateWindow (xdisplay, root_xwin, -100, -100, 1, 1, 0, xvisinfo->depth, CopyFromParent, xvisinfo->visual, CWOverrideRedirect | CWColormap | CWBorderPixel, &attrs); /* Try and create a GLXWindow to use with extensions dependent on * GLX versions >= 1.3 that don't accept regular X Windows as GLX * drawables. */ if (glXQueryVersion (backend_x11->xdpy, &major, &minor) && major == 1 && minor >= 3) { backend_glx->dummy_glxwin = glXCreateWindow (backend_x11->xdpy, config, backend_glx->dummy_xwin, NULL); } if (backend_glx->dummy_glxwin) dummy_drawable = backend_glx->dummy_glxwin; else dummy_drawable = backend_glx->dummy_xwin; CLUTTER_NOTE (BACKEND, "Selecting dummy 0x%x for the GLX context", (unsigned int) dummy_drawable); glXMakeContextCurrent (xdisplay, dummy_drawable, dummy_drawable, backend_glx->gl_context); XFree (xvisinfo); if (clutter_x11_untrap_x_errors ()) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to select the newly created GLX context"); return FALSE; } return TRUE; }
/** * clutter_x11_texture_pixmap_sync_window: * @texture: the texture to bind * * Resets the texture's pixmap from its window, perhaps in response to the * pixmap's invalidation as the window changed size. * * Since: 0.8 **/ void clutter_x11_texture_pixmap_sync_window (ClutterX11TexturePixmap *texture) { ClutterX11TexturePixmapPrivate *priv; Pixmap pixmap; g_return_if_fail (CLUTTER_X11_IS_TEXTURE_PIXMAP (texture)); priv = texture->priv; if (priv->destroyed) return; if (!clutter_x11_has_composite_extension()) { clutter_x11_texture_pixmap_set_pixmap (texture, priv->window); return; } if (priv->window) { XWindowAttributes attr; Display *dpy = clutter_x11_get_default_display (); gboolean mapped, notify_x, notify_y, notify_override_redirect; /* We may get a BadMatch error here if the window is not mapped. * If so, ignore it - pixmap should be set to None in that case anyway */ XSync (dpy, FALSE); clutter_x11_trap_x_errors (); XGetWindowAttributes (dpy, priv->window, &attr); mapped = attr.map_state == IsViewable; if (mapped) pixmap = XCompositeNameWindowPixmap (dpy, priv->window); else pixmap = None; XSync (dpy, FALSE); if (clutter_x11_untrap_x_errors() && pixmap!=None) g_warning("%s: Got X error but pixmap is still set", __FUNCTION__); notify_x = attr.x != priv->window_x; notify_y = attr.y != priv->window_y; notify_override_redirect = attr.override_redirect != priv->override_redirect; priv->window_x = attr.x; priv->window_y = attr.y; priv->override_redirect = attr.override_redirect; g_object_ref (texture); /* guard against unparent */ if (pixmap) { clutter_x11_texture_pixmap_set_pixmap (texture, pixmap); priv->owns_pixmap = TRUE; } clutter_x11_texture_pixmap_set_mapped (texture, mapped); /* could do more clever things with a signal, i guess.. */ if (notify_override_redirect) g_object_notify (G_OBJECT (texture), "window-override-redirect"); if (notify_x) g_object_notify (G_OBJECT (texture), "window-x"); if (notify_y) g_object_notify (G_OBJECT (texture), "window-y"); g_object_unref (texture); } }
static ClutterTranslateReturn clutter_stage_x11_translate_event (ClutterEventTranslator *translator, gpointer native, ClutterEvent *event) { ClutterStageX11 *stage_x11; ClutterStageCogl *stage_cogl; ClutterTranslateReturn res = CLUTTER_TRANSLATE_CONTINUE; ClutterBackendX11 *backend_x11; Window stage_xwindow; XEvent *xevent = native; ClutterStage *stage; stage_cogl = clutter_x11_get_stage_window_from_window (xevent->xany.window); if (stage_cogl == NULL) return CLUTTER_TRANSLATE_CONTINUE; stage = stage_cogl->wrapper; stage_x11 = CLUTTER_STAGE_X11 (stage_cogl); backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend); stage_xwindow = stage_x11->xwin; switch (xevent->type) { case ConfigureNotify: if (!stage_x11->is_foreign_xwin) { gboolean size_changed = FALSE; CLUTTER_NOTE (BACKEND, "ConfigureNotify[%x] (%d, %d)", (unsigned int) stage_x11->xwin, xevent->xconfigure.width, xevent->xconfigure.height); /* When fullscreen, we'll keep the xwin_width/height variables to track the old size of the window and we'll assume all ConfigureNotifies constitute a size change */ if (_clutter_stage_is_fullscreen (stage)) size_changed = TRUE; else if ((stage_x11->xwin_width != xevent->xconfigure.width) || (stage_x11->xwin_height != xevent->xconfigure.height)) { size_changed = TRUE; 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_cogl->wrapper, CLUTTER_IN_RESIZE); if (size_changed) { /* XXX: This is a workaround for a race condition when * resizing windows while there are in-flight * glXCopySubBuffer blits happening. * * The problem stems from the fact that rectangles for the * blits are described relative to the bottom left of the * window and because we can't guarantee control over the X * window gravity used when resizing so the gravity is * typically NorthWest not SouthWest. * * This means if you grow a window vertically the server * will make sure to place the old contents of the window * at the top-left/north-west of your new larger window, but * that may happen asynchronous to GLX preparing to do a * blit specified relative to the bottom-left/south-west of * the window (based on the old smaller window geometry). * * When the GLX issued blit finally happens relative to the * new bottom of your window, the destination will have * shifted relative to the top-left where all the pixels you * care about are so it will result in a nasty artefact * making resizing look very ugly! * * We can't currently fix this completely, in-part because * the window manager tends to trample any gravity we might * set. This workaround instead simply disables blits for a * while if we are notified of any resizes happening so if * the user is resizing a window via the window manager then * they may see an artefact for one frame but then we will * fallback to redrawing the full stage until the cooling * off period is over. */ if (stage_x11->clipped_redraws_cool_off) g_source_remove (stage_x11->clipped_redraws_cool_off); stage_x11->clipped_redraws_cool_off = clutter_threads_add_timeout (1000, clipped_redraws_cool_off_cb, stage_x11); /* 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() call 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. */ clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); /* 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); } } 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) { gboolean is_fullscreen = FALSE; Atom *atoms = (Atom *) data; gulong i; for (i = 0; i < n_items; i++) { if (atoms[i] == backend_x11->atom_NET_WM_STATE_FULLSCREEN) fullscreen_set = TRUE; } is_fullscreen = _clutter_stage_is_fullscreen (stage_cogl->wrapper); if (fullscreen_set != is_fullscreen) { if (fullscreen_set) _clutter_stage_update_state (stage_cogl->wrapper, 0, CLUTTER_STAGE_STATE_FULLSCREEN); else _clutter_stage_update_state (stage_cogl->wrapper, CLUTTER_STAGE_STATE_FULLSCREEN, 0); } XFree (data); } } break; case FocusIn: if (!_clutter_stage_is_activated (stage_cogl->wrapper)) { _clutter_stage_update_state (stage_cogl->wrapper, 0, CLUTTER_STAGE_STATE_ACTIVATED); } break; case FocusOut: if (_clutter_stage_is_activated (stage_cogl->wrapper)) { _clutter_stage_update_state (stage_cogl->wrapper, CLUTTER_STAGE_STATE_ACTIVATED, 0); } break; case EnterNotify: #if HAVE_XFIXES if (!stage_x11->is_cursor_visible && !stage_x11->cursor_hidden_xfixes) { XFixesHideCursor (backend_x11->xdpy, stage_x11->xwin); stage_x11->cursor_hidden_xfixes = TRUE; } #endif break; case LeaveNotify: #if HAVE_XFIXES if (stage_x11->cursor_hidden_xfixes) { XFixesShowCursor (backend_x11->xdpy, stage_x11->xwin); stage_x11->cursor_hidden_xfixes = FALSE; } #endif break; case Expose: { XExposeEvent *expose = (XExposeEvent *) xevent; cairo_rectangle_int_t clip; CLUTTER_NOTE (EVENT, "expose for stage: %s[%p], win:0x%x - " "redrawing area (x: %d, y: %d, width: %d, height: %d)", _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), stage, (unsigned int) stage_xwindow, expose->x, expose->y, expose->width, expose->height); clip.x = expose->x; clip.y = expose->y; clip.width = expose->width; clip.height = expose->height; clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &clip); } break; case DestroyNotify: CLUTTER_NOTE (EVENT, "Destroy notification received for stage %s[%p], win:0x%x", _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), stage, (unsigned int) stage_xwindow); event->any.type = CLUTTER_DESTROY_NOTIFY; event->any.stage = stage; res = CLUTTER_TRANSLATE_QUEUE; break; case ClientMessage: CLUTTER_NOTE (EVENT, "Client message for stage %s[%p], win:0x%x", _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), stage, (unsigned int) stage_xwindow); if (handle_wm_protocols_event (backend_x11, stage_x11, xevent)) { event->any.type = CLUTTER_DELETE; event->any.stage = stage; res = CLUTTER_TRANSLATE_QUEUE; } break; case MappingNotify: CLUTTER_NOTE (EVENT, "Refresh keyboard mapping"); XRefreshKeyboardMapping (&xevent->xmapping); backend_x11->keymap_serial += 1; res = CLUTTER_TRANSLATE_REMOVE; break; default: res = CLUTTER_TRANSLATE_CONTINUE; break; } return res; }
static void clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture) { ClutterGLXTexturePixmapPrivate *priv = texture->priv; GLXPixmap glx_pixmap = None; int attribs[7], i = 0, mipmap = 0; GLXFBConfig *fbconfig; Display *dpy; guint depth; Pixmap pixmap; guint pixmap_width, pixmap_height; ClutterBackendGLX *backend_glx; ClutterTextureQuality quality; CLUTTER_NOTE (TEXTURE, "Creating GLXPixmap"); backend_glx = CLUTTER_BACKEND_GLX(clutter_get_default_backend ()); dpy = clutter_x11_get_default_display (); if (priv->use_fallback == TRUE || !clutter_glx_texture_pixmap_using_extension (texture)) goto cleanup; priv->use_fallback = FALSE; g_object_get (texture, "pixmap-width", &pixmap_width, "pixmap-height", &pixmap_height, "pixmap-depth", &depth, "pixmap", &pixmap, NULL); if (!pixmap) { goto cleanup; } fbconfig = get_fbconfig_for_depth (depth); if (!fbconfig) { g_warning ("Could not find an FBConfig for selected pixmap"); goto cleanup; } attribs[i++] = GLX_TEXTURE_FORMAT_EXT; if (depth == 24) { attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT; } else if (depth == 32) { attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT; } else { g_warning ("Pixmap with depth bellow 24 are not supported"); goto cleanup; } quality = clutter_texture_get_filter_quality (CLUTTER_TEXTURE (texture)); if (quality == CLUTTER_TEXTURE_QUALITY_HIGH) mipmap = 1; attribs[i++] = GLX_MIPMAP_TEXTURE_EXT; attribs[i++] = mipmap; attribs[i++] = GLX_TEXTURE_TARGET_EXT; attribs[i++] = GLX_TEXTURE_2D_EXT; attribs[i++] = None; clutter_x11_trap_x_errors (); glx_pixmap = glXCreatePixmap (dpy, *fbconfig, pixmap, attribs); XSync (dpy, FALSE); if (clutter_x11_untrap_x_errors ()) { CLUTTER_NOTE (TEXTURE, "Failed to create GLXPixmap"); /* Make sure we don't think the call actually succeeded */ glx_pixmap = None; } g_free (fbconfig); cleanup: if (priv->glx_pixmap) clutter_glx_texture_pixmap_free_glx_pixmap (texture); if (glx_pixmap != None) { priv->glx_pixmap = glx_pixmap; create_cogl_texture (CLUTTER_TEXTURE (texture), pixmap_width, pixmap_height); CLUTTER_NOTE (TEXTURE, "Created GLXPixmap"); return; } else { priv->use_fallback = TRUE; priv->glx_pixmap = None; /* Some fucky logic here - we've fallen back and need to make sure * we realize here.. */ clutter_actor_realize (CLUTTER_ACTOR (texture)); } }
/* 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); } }
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); } }