1 /* This file is part of SDL_haa - SDL addon for Hildon Animation Actors
2 * Copyright (C) 2010 Javier S. Pedro
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA or see <http://www.gnu.org/licenses/>.
26 #include <X11/Xutil.h>
27 #include <X11/Xatom.h>
29 #include <SDL_syswm.h>
32 #include <X11/extensions/XShm.h>
40 typedef struct HAA_ActorPriv {
43 Window window, parent;
49 XShmSegmentInfo shminfo;
52 struct HAA_ActorPriv *prev, *next;
55 static Bool initialized = False;
57 static Display *display;
58 static Window parent_window;
59 static HAA_ActorPriv *first = NULL, *last = NULL;
62 static int shm_major, shm_minor;
63 static Bool shm_pixmaps;
66 static const Bool have_shm = False;
69 int HAA_Init(Uint32 flags)
73 SDL_VERSION(&info.version);
74 if (SDL_GetWMInfo(&info) != 1) {
75 SDL_SetError("SDL_haa is incompatible with this SDL version");
79 display = info.info.x11.display;
80 if (flags & SDL_FULLSCREEN) {
81 parent_window = info.info.x11.fswindow;
83 parent_window = info.info.x11.wmwindow;
88 XInternAtoms(display, (char**)atom_names, ATOM_COUNT, True, atom_values);
92 have_shm = XShmQueryVersion(display, &shm_major, &shm_minor, &shm_pixmaps);
95 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
106 static HAA_ActorPriv* find_actor_for_window(Window w)
110 for (a = first; a; a = a->next) {
111 if (a->window == w) {
119 static void actor_send_message(HAA_ActorPriv* actor, Atom message_type,
120 Uint32 l0, Uint32 l1, Uint32 l2, Uint32 l3, Uint32 l4)
122 Window window = actor->window;
123 XEvent event = { 0 };
125 event.xclient.type = ClientMessage;
126 event.xclient.window = window;
127 event.xclient.message_type = message_type;
128 event.xclient.format = 32;
129 event.xclient.data.l[0] = l0;
130 event.xclient.data.l[1] = l1;
131 event.xclient.data.l[2] = l2;
132 event.xclient.data.l[3] = l3;
133 event.xclient.data.l[4] = l4;
135 XSendEvent(display, window, True,
140 static void HAA_ReparentAllTo(Window new_parent)
142 if (new_parent != parent_window) {
144 /* video mode has changed */
145 parent_window = new_parent;
147 /* unmap all actors that were already ready */
148 for (a = first; a; a = a->next) {
149 a->parent = parent_window;
151 XUnmapWindow(display, a->window);
155 XSync(display, False);
157 /* now remap and reconfigure all actors */
158 for (a = first; a; a = a->next) {
160 XMapWindow(display, a->window);
162 a->p.pending |= HAA_PENDING_EVERYTHING;
164 a->p.pending |= HAA_PENDING_PARENT | HAA_PENDING_SHOW;
172 static void HAA_AutoReparent()
175 SDL_Surface *screen = SDL_GetVideoSurface();
180 SDL_VERSION(&info.version);
181 int res = SDL_GetWMInfo(&info);
184 if (screen->flags & SDL_FULLSCREEN) {
185 new_parent = info.info.x11.fswindow;
187 new_parent = info.info.x11.wmwindow;
190 HAA_ReparentAllTo(new_parent);
193 static void HAA_Pending(HAA_ActorPriv* actor)
195 const Uint16 pending = actor->p.pending;
197 if (!actor->ready) return; //Enqueue and wait
199 if (pending & HAA_PENDING_ANCHOR) {
200 actor_send_message(actor,
201 ATOM(_HILDON_ANIMATION_CLIENT_MESSAGE_ANCHOR),
202 actor->p.gravity, actor->p.anchor_x, actor->p.anchor_y, 0, 0);
204 if (pending & HAA_PENDING_POSITION) {
205 actor_send_message(actor,
206 ATOM(_HILDON_ANIMATION_CLIENT_MESSAGE_POSITION),
207 actor->p.position_x, actor->p.position_y, actor->p.depth, 0, 0);
210 if (pending & HAA_PENDING_ROTATION_X) {
211 actor_send_message(actor,
212 ATOM(_HILDON_ANIMATION_CLIENT_MESSAGE_ROTATION),
214 actor->p.x_rotation_angle,
215 0, actor->p.x_rotation_y, actor->p.x_rotation_z);
217 if (pending & HAA_PENDING_ROTATION_Y) {
218 actor_send_message(actor,
219 ATOM(_HILDON_ANIMATION_CLIENT_MESSAGE_ROTATION),
221 actor->p.y_rotation_angle,
222 actor->p.y_rotation_x, 0, actor->p.y_rotation_z);
224 if (pending & HAA_PENDING_ROTATION_Z) {
225 actor_send_message(actor,
226 ATOM(_HILDON_ANIMATION_CLIENT_MESSAGE_ROTATION),
228 actor->p.x_rotation_angle,
229 actor->p.z_rotation_x, actor->p.z_rotation_y, 0);
232 if (pending & HAA_PENDING_SCALE) {
233 actor_send_message(actor,
234 ATOM(_HILDON_ANIMATION_CLIENT_MESSAGE_SCALE),
235 actor->p.scale_x, actor->p.scale_y, 0, 0, 0);
238 if (pending & HAA_PENDING_PARENT) {
239 actor_send_message(actor,
240 ATOM(_HILDON_ANIMATION_CLIENT_MESSAGE_PARENT),
241 actor->parent, 0, 0, 0, 0);
243 if (pending & HAA_PENDING_SHOW) {
244 actor_send_message(actor,
245 ATOM(_HILDON_ANIMATION_CLIENT_MESSAGE_SHOW),
246 actor->p.visible, actor->p.opacity, 0, 0, 0);
249 actor->p.pending = HAA_PENDING_NOTHING;
252 static void sdl_expose()
254 SDL_Event events[32];
256 /* Pull out all old refresh events */
257 SDL_PeepEvents(events, sizeof(events)/sizeof(events[0]),
258 SDL_GETEVENT, SDL_VIDEOEXPOSEMASK);
260 /* Post the event, if desired */
261 if ( SDL_EventState(SDL_VIDEOEXPOSE, SDL_QUERY) == SDL_ENABLE ) {
263 event.type = SDL_VIDEOEXPOSE;
264 SDL_PushEvent(&event);
268 static void actor_update_ready(HAA_ActorPriv* actor)
270 Window window = actor->window;
274 unsigned long nitems, bytes_after;
275 unsigned char *prop = NULL;
277 status = XGetWindowProperty(display, window,
278 ATOM(_HILDON_ANIMATION_CLIENT_READY), 0, 32,
280 &actual_type, &actual_format,
281 &nitems, &bytes_after, &prop);
287 if (status != Success || actual_type != XA_ATOM ||
288 actual_format != 32 || nitems != 1) {
294 /* Ready flag already set, which means hildon-desktop just restarted */
295 XUnmapWindow(display, window);
296 XSync(display, False);
297 XMapWindow(display, window);
298 XSync(display, False);
299 SDL_Delay(500); /* Give the WM some time to relax. */
301 /* Next Flip will resend every setting */
302 actor->p.pending = HAA_PENDING_EVERYTHING;
308 /* Send all pending messages now */
315 int HAA_FilterEvent(const SDL_Event *event)
317 switch (event->type) {
319 case SDL_SYSWMEVENT: {
320 const XEvent *e = &event->syswm.msg->event.xevent;
323 if (e->xproperty.atom == ATOM(_HILDON_ANIMATION_CLIENT_READY)) {
324 HAA_ActorPriv* actor =
325 find_actor_for_window(e->xproperty.window);
327 actor_update_ready(actor);
337 /* For reasons unknown, we seem to receive this on fullscreen events */
338 const SDL_keysym *key = &event->key.keysym;
339 if (key->scancode == 0 && key->sym == SDLK_NUMLOCK &&
340 key->mod == KMOD_NUM) {
341 /* Either task switched or fullscreen invoked */
352 HAA_Actor* HAA_CreateActor(Uint32 flags,
353 int width, int height, int bitsPerPixel)
355 HAA_ActorPriv *actor = malloc(sizeof(HAA_ActorPriv));
357 SDL_Error(SDL_ENOMEM);
361 /* Default actor settings */
362 actor->p.position_x = 0;
363 actor->p.position_y = 0;
365 actor->p.visible = 0;
366 actor->p.opacity = 255;
367 actor->parent = parent_window;
368 actor->p.scale_x = 1 << 16;
369 actor->p.scale_y = 1 << 16;
370 actor->p.gravity = HAA_GRAVITY_NONE;
371 actor->p.anchor_x = 0;
372 actor->p.anchor_y = 0;
373 actor->p.x_rotation_angle = 0;
374 actor->p.x_rotation_y = 0;
375 actor->p.x_rotation_z = 0;
376 actor->p.y_rotation_angle = 0;
377 actor->p.y_rotation_x = 0;
378 actor->p.y_rotation_z = 0;
379 actor->p.z_rotation_angle = 0;
380 actor->p.z_rotation_x = 0;
381 actor->p.z_rotation_y = 0;
383 HAA_PENDING_POSITION | HAA_PENDING_SCALE | HAA_PENDING_PARENT;
385 /* Select the X11 visual */
386 int screen = DefaultScreen(display);
387 Window root = RootWindow(display, screen);
391 if (!XMatchVisualInfo(display, screen, bitsPerPixel, TrueColor, &vinfo)) {
392 /* Not matched; Use the default visual instead */
396 vinfo.screen = screen;
397 xvi = XGetVisualInfo(display, VisualScreenMask, &vinfo, &numVisuals);
403 actor->visual = vinfo.visual;
405 if (vinfo.visual != DefaultVisual(display, screen)) {
406 /* Allocate a private color map. */
407 actor->colormap = XCreateColormap(display, root,
408 vinfo.visual, AllocNone);
413 /* Create X11 window for actor */
414 XSetWindowAttributes attr;
415 unsigned long attrmask = CWBorderPixel | CWBackPixel | CWBitGravity;
416 attr.background_pixel = BlackPixel(display, screen);
417 attr.border_pixel = attr.background_pixel;
418 attr.bit_gravity = ForgetGravity;
419 if (actor->colormap) {
420 attr.colormap = actor->colormap;
421 attrmask |= CWColormap;
424 Window window = actor->window = XCreateWindow(display, root,
425 0, 0, width, height, 0, vinfo.depth,
426 InputOutput, vinfo.visual,
429 XStoreName(display, window, "sdl_haa window");
431 Atom atom = ATOM(_HILDON_WM_WINDOW_TYPE_ANIMATION_ACTOR);
432 XChangeProperty(display, window, ATOM(_NET_WM_WINDOW_TYPE),
433 XA_ATOM, 32, PropModeReplace,
434 (unsigned char *) &atom, 1);
436 /* Setup the X Image */
438 image = actor->image = XShmCreateImage(display, vinfo.visual,
439 vinfo.depth, ZPixmap, NULL, &actor->shminfo, width, height);
441 SDL_SetError("Cannot create XSHM image");
445 actor->shminfo.shmid = shmget(IPC_PRIVATE,
446 image->bytes_per_line * image->height, IPC_CREAT|0777);
447 if (actor->shminfo.shmid < 0) {
448 SDL_SetError("Failed to get shared memory");
449 XDestroyImage(image);
453 actor->shminfo.shmaddr = shmat(actor->shminfo.shmid, NULL, 0);
454 if (!actor->shminfo.shmaddr) {
455 SDL_SetError("Failed to attach shared memory");
456 XDestroyImage(image);
457 shmctl(actor->shminfo.shmid, IPC_RMID, 0);
461 actor->shminfo.readOnly = True;
462 if (!XShmAttach(display, &actor->shminfo)) {
463 SDL_SetError("Failed to attach shared memory image");
464 XDestroyImage(image);
465 shmdt(actor->shminfo.shmaddr);
466 shmctl(actor->shminfo.shmid, IPC_RMID, 0);
470 /* Ensure attachment is done */
471 XSync(display, False);
473 /* Nobody else needs it now */
474 shmctl(actor->shminfo.shmid, IPC_RMID, 0);
476 pixels = actor->shminfo.shmaddr;
477 image->data = (char*) pixels;
479 pixels = malloc(width * height * (vinfo.depth / 8));
481 SDL_SetError("Cannot allocate image");
484 image = actor->image = XCreateImage(display, vinfo.visual,
485 vinfo.depth, ZPixmap, 0, (char*) pixels, width, height, 8, 0);
487 SDL_SetError("Cannot create X image");
492 /* Guess alpha mask */
494 if (image->depth == 32) {
495 Amask = ~(vinfo.red_mask | vinfo.green_mask | vinfo.blue_mask);
499 GC gc = actor->gc = XCreateGC(display, window, 0, NULL);
500 XSetForeground(display, gc, 0xFFFFFFFFU);
502 /** Create SDL texture for actor */
503 actor->p.surface = SDL_CreateRGBSurfaceFrom(pixels,
504 image->width, image->height, image->depth, image->bytes_per_line,
505 vinfo.red_mask, vinfo.green_mask, vinfo.blue_mask, Amask);
507 if (!actor->p.surface) {
508 /* SDL Error already set */
513 XSelectInput(display, window, PropertyChangeMask);
514 XMapWindow(display, window);
516 /* Add to actor linked list */
518 assert(last == NULL);
519 actor->next = actor->prev = NULL;
520 first = last = actor;
528 XSync(display, False);
529 return (HAA_Actor*) actor;
532 XFreeGC(display, gc);
535 XShmDetach(display, &actor->shminfo);
536 XDestroyImage(image);
537 shmdt(actor->shminfo.shmaddr);
539 XDestroyImage(image);
542 XDestroyWindow(display, window);
543 if (actor->colormap) XFreeColormap(display, actor->colormap);
547 XSync(display, True);
551 void HAA_FreeActor(HAA_Actor* a)
553 HAA_ActorPriv* actor = (HAA_ActorPriv*)a;
556 XFreeGC(display, actor->gc);
558 XShmDetach(display, &actor->shminfo);
559 XDestroyImage(actor->image);
560 shmdt(actor->shminfo.shmaddr);
561 XDestroyWindow(display, actor->window);
563 XDestroyImage(actor->image);
566 XFreeColormap(display, actor->colormap);
567 SDL_FreeSurface(actor->p.surface);
569 if (first == actor && last == actor) {
570 assert(!actor->next && !actor->prev);
573 } else if (last == actor) {
574 assert(!actor->next && actor->prev);
577 } else if (first == actor) {
578 assert(actor->next && !actor->prev);
582 assert(actor->next && actor->prev);
583 actor->prev->next = actor->next;
584 actor->next->prev = actor->prev;
592 int HAA_Commit(HAA_Actor* a)
594 HAA_ActorPriv* actor = (HAA_ActorPriv*)a;
597 XSync(display, False);
602 int HAA_Flip(HAA_Actor* a)
604 HAA_ActorPriv* actor = (HAA_ActorPriv*)a;
605 Window window = actor->window;
607 XImage *image = actor->image;
610 XShmPutImage(display, window, gc, image,
611 0, 0, 0, 0, image->width, image->height, False);
613 XPutImage(display, window, gc, image,
614 0, 0, 0, 0, image->width, image->height);
618 XSync(display, False);