Some fixes to the code to support running on an externally
authorLuigi Rizzo <rizzo@icir.org>
Mon, 19 May 2008 14:22:04 +0000 (14:22 +0000)
committerLuigi Rizzo <rizzo@icir.org>
Mon, 19 May 2008 14:22:04 +0000 (14:22 +0000)
supplied window.

SDL (at least recent 1.2.x versions) has the ability to run the
graphic output into an externally supplied window, whose ID in the
environment variable SDL_WINDOWID. Ideally, applications should
run unchanged irrespective of who creates the window. Unfortunately,
SDL does not subscribe to mouse, key and resize events on externally
supplied windows, so we need to do ask for these events explicitly.

On passing, also add some code to handle SDL_ACTIVEEVENT so if
the X11 window is killed while we are active, we call
"stop now" to terminate the asterisk instance.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@117052 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/console_gui.c

index 7379188..424b31f 100644 (file)
@@ -55,10 +55,16 @@ static int keypad_cfg_read(struct gui_info *gui, const char *val)   { return 0; }
 #else /* HAVE_SDL, the real rendering code */
 
 #include <SDL/SDL.h>
+#include <SDL/SDL_syswm.h>
 #ifdef HAVE_SDL_IMAGE
 #include <SDL/SDL_image.h>      /* for loading images */
 #endif
 
+#ifdef HAVE_X11
+/* Need to hook into X for SDL_WINDOWID handling */
+#include <X11/Xlib.h>
+#endif
+
 enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
 struct keypad_entry {
         int c;  /* corresponding character */
@@ -563,9 +569,27 @@ static void eventhandler(struct video_desc *env, const char *caption)
                                ev[i].type,  ev[i].button.x,  ev[i].button.y);
 #endif
                        switch (ev[i].type) {
+                       default:
+                               ast_log(LOG_WARNING, "------ event %d at %d %d\n",
+                                       ev[i].type,  ev[i].button.x,  ev[i].button.y);
+                               break;
+
+                       case SDL_ACTIVEEVENT:
+                               ast_log(LOG_WARNING, "------ active gain %d state 0x%x\n",
+                                       ev[i].active.gain,  ev[i].active.state);
+                               if (ev[i].active.gain == 0 && ev[i].active.state & SDL_APPACTIVE) {
+                                       ast_log(LOG_WARNING, "/* somebody has killed us ? */");
+                                       ast_cli_command(gui->outfd, "stop now");
+                               }
+                               break;
+
+                       case SDL_KEYUP: /* ignore, for the time being */
+                               break;
+
                        case SDL_KEYDOWN:
                                handle_keyboard_input(env, &ev[i].key.keysym);
                                break;
+
                        case SDL_MOUSEMOTION:
                        case SDL_MOUSEBUTTONUP:
                                if (drag->drag_window == DRAG_LOCAL) {
@@ -790,6 +814,11 @@ static void sdl_setup(struct video_desc *env)
        /* We want at least 16bpp to support YUV overlays.
         * E.g with SDL_VIDEODRIVER = aalib the default is 8
         */
+       if (!info || !info->vfmt) {
+               ast_log(LOG_WARNING, "Bad SDL_GetVideoInfo - %s\n",
+                        SDL_GetError());
+               return;
+       }
        depth = info->vfmt->BitsPerPixel;
        if (depth < 16)
                depth = 16;
@@ -813,12 +842,105 @@ static void sdl_setup(struct video_desc *env)
        maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h);
        maxw += 4 * BORDER;
        maxh += 2 * BORDER;
+       /* XXX warning, here it might crash if SDL_WINDOWID is set badly */
        gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
        if (!gui->screen) {
                ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
                goto no_sdl;
        }
 
+#ifdef HAVE_X11
+       /*
+        * Annoying as it may be, if SDL_WINDOWID is set, SDL does
+        * not grab keyboard/mouse events or expose or other stuff,
+        * and it does not handle resize either.
+        * So we need to implement workarounds here.
+        */
+    do {
+       /* First, handle the event mask */
+       XWindowAttributes attr;
+        long want;
+        SDL_SysWMinfo info;
+       Display *SDL_Display;
+        Window win;
+
+       const char *e = getenv("SDL_WINDOWID");
+       if (ast_strlen_zero(e))  /* no external window, don't bother doing this */
+               break;
+        SDL_VERSION(&info.version); /* it is important to set the version */
+        if (SDL_GetWMInfo(&info) != 1) {
+                fprintf(stderr, "no wm info\n");
+                break;
+        }
+       SDL_Display = info.info.x11.display;
+       if (SDL_Display == NULL)
+               break;
+        win = info.info.x11.window;
+
+       /*
+        * A list of events we want.
+        * Leave ResizeRedirectMask to the parent.
+        */
+        want = KeyPressMask | KeyReleaseMask | ButtonPressMask |
+                           ButtonReleaseMask | EnterWindowMask |
+                           LeaveWindowMask | PointerMotionMask |
+                           Button1MotionMask |
+                           Button2MotionMask | Button3MotionMask |
+                           Button4MotionMask | Button5MotionMask |
+                           ButtonMotionMask | KeymapStateMask |
+                           ExposureMask | VisibilityChangeMask |
+                           StructureNotifyMask | /* ResizeRedirectMask | */
+                           SubstructureNotifyMask | SubstructureRedirectMask |
+                           FocusChangeMask | PropertyChangeMask |
+                           ColormapChangeMask | OwnerGrabButtonMask;
+
+        bzero(&attr, sizeof(attr));
+       XGetWindowAttributes(SDL_Display, win, &attr);
+
+       /* the following events can be delivered only to one client.
+        * So check which ones are going to someone else, and drop
+        * them from our request.
+        */
+       {
+       /* ev are the events for a single recipient */
+       long ev = ButtonPressMask | ResizeRedirectMask |
+                       SubstructureRedirectMask;
+        ev &= (attr.all_event_masks & ~attr.your_event_mask);
+       /* now ev contains 1 for single-recipient events owned by others.
+        * We must clear those bits in 'want'
+        * and then add the bits in 'attr.your_event_mask' to 'want'
+        */
+       want &= ~ev;
+       want |= attr.your_event_mask;
+       }
+       XSelectInput(SDL_Display, win, want);
+
+       /* Second, handle resize.
+        * We do part of the things that X11Resize does,
+        * but also generate a ConfigureNotify event so
+        * the owner of the window has a chance to do something
+        * with it.
+        */
+       XResizeWindow(SDL_Display, win, maxw, maxh);
+       {
+       XConfigureEvent ce = {
+               .type = ConfigureNotify,
+               .serial = 0,
+               .send_event = 1,        /* TRUE */
+               .display = SDL_Display,
+               .event = win,
+               .window = win,
+               .x = 0,
+               .y = 0,
+               .width = maxw,
+               .height = maxh,
+               .border_width = 0,
+               .above = 0,
+               .override_redirect = 0 };
+       XSendEvent(SDL_Display, win, 1 /* TRUE */, StructureNotifyMask, (XEvent *)&ce);
+       }
+    } while (0);
+#endif /* HAVE_X11 */
        SDL_WM_SetCaption("Asterisk console Video Output", NULL);
        if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
                        env->rem_dpy.w, env->rem_dpy.h, BORDER, BORDER))