~ubuntu-branches/ubuntu/maverick/zapping/maverick

« back to all changes in this revision

Viewing changes to src/overlay.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Holbach
  • Date: 2005-03-08 23:19:08 UTC
  • mfrom: (2.1.1 sarge)
  • Revision ID: james.westby@ubuntu.com-20050308231908-oip7rfv6lcmo8c0e
Tags: 0.9.2-2ubuntu1
Rebuilt for Python transition (2.3 -> 2.4)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Zapping (TV viewer for the Gnome Desktop)
 
1
/*
 
2
 * Zapping (TV viewer for the Gnome Desktop)
 
3
 *
2
4
 * Copyright (C) 2000-2001 I�aki Garc�a Etxebarria
 
5
 * Copyright (C) 2003 Michael H. Schimek
3
6
 *
4
7
 * This program is free software; you can redistribute it and/or modify
5
8
 * it under the terms of the GNU General Public License as published by
16
19
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
20
 */
18
21
 
 
22
#include "site_def.h"
 
23
 
19
24
#ifdef HAVE_CONFIG_H
20
 
#  include <config.h>
 
25
#  include "config.h"
21
26
#endif
22
27
 
23
28
#include <gtk/gtk.h>
24
29
#include <gdk/gdkx.h>
25
30
#include <tveng.h>
26
31
 
 
32
#define ZCONF_DOMAIN "/zapping/options/main/"
 
33
#include "zconf.h"
27
34
#include "x11stuff.h"
28
35
#include "zmisc.h"
29
 
#include "zvbi.h"
30
36
#include "overlay.h"
31
37
#include "osd.h"
 
38
#include "globals.h"
 
39
#include "zvideo.h"
 
40
 
 
41
/* This code clips DMA overlay into video memory against X window
 
42
   boundaries. Which is really the job of the X server, but without
 
43
   XVideo extension we have little choice. If XVideo is available
 
44
   it merely adjusts the overlay to fill the video window. Actually
 
45
   all this should be integrated into zvideo.c. */
 
46
 
 
47
/* TODO:
 
48
   + Special mode for devices without clipping. Hint: think twice.
 
49
   + Matte option (clip out WSS, GCR).
 
50
   + Source rectangle, if supported by hardware (e.g. zoom function).
 
51
 */
 
52
 
 
53
#ifndef OVERLAY_LOG_FP
 
54
#define OVERLAY_LOG_FP 0
 
55
#endif
 
56
 
 
57
#ifndef OVERLAY_EVENT_LOG_FP
 
58
#define OVERLAY_EVENT_LOG_FP 0
 
59
#endif
 
60
 
 
61
#ifndef OVERLAY_DUMP_CLIPS
 
62
#define OVERLAY_DUMP_CLIPS 0
 
63
#endif
 
64
 
 
65
#ifndef OVERLAY_CHROMA_TEST
 
66
#define OVERLAY_CHROMA_TEST 0
 
67
#endif
32
68
 
33
69
#define CLEAR_TIMEOUT 50 /* ms for the clear window timeout */
34
70
 
35
 
/* event names for debugging. event_str = events[event->type+1] */
36
 
#if 0 /* just for debugging */
37
 
static gchar * events[] =
38
 
{
39
 
  "GDK_NOTHING",
40
 
  "GDK_DELETE",
41
 
  "GDK_DESTROY",
42
 
  "GDK_EXPOSE",
43
 
  "GDK_MOTION_NOTIFY",
44
 
  "GDK_BUTTON_PRESS",
45
 
  "GDK_2BUTTON_PRESS",
46
 
  "GDK_3BUTTON_PRESS",
47
 
  "GDK_BUTTON_RELEASE",
48
 
  "GDK_KEY_PRESS",
49
 
  "GDK_KEY_RELEASE",
50
 
  "GDK_ENTER_NOTIFY",
51
 
  "GDK_LEAVE_NOTIFY",
52
 
  "GDK_FOCUS_CHANGE",
53
 
  "GDK_CONFIGURE",
54
 
  "GDK_MAP",
55
 
  "GDK_UNMAP",
56
 
  "GDK_PROPERTY_NOTIFY",
57
 
  "GDK_SELECTION_CLEAR",
58
 
  "GDK_SELECTION_REQUEST",
59
 
  "GDK_SELECTION_NOTIFY",
60
 
  "GDK_PROXIMITY_IN",
61
 
  "GDK_PROXIMITY_OUT",
62
 
  "GDK_DRAG_ENTER",
63
 
  "GDK_DRAG_LEAVE",
64
 
  "GDK_DRAG_MOTION",
65
 
  "GDK_DRAG_STATUS",
66
 
  "GDK_DROP_START",
67
 
  "GDK_DROP_FINISHED",
68
 
  "GDK_CLIENT_EVENT",
69
 
  "GDK_VISIBILITY_NOTIFY",
70
 
  "GDK_NO_EXPOSE"
71
 
};
72
 
#endif
73
 
 
74
 
/* Saves some info about the tv_screen */
75
71
static struct {
76
 
  gint x, y, w, h; /* geometry */
77
 
  gboolean visible; /* If it is visible */
78
 
  GtkWidget * window; /* The window we will be overlaying to */
79
 
  GtkWidget * main_window; /* The toplevel window .window is in */
80
 
  tveng_device_info * info; /* The overlaying V4L device */
81
 
  struct tveng_clip * clips; /* Clip rectangles */
82
 
  gint clipcount; /* Number of clips in clips */
83
 
  gboolean clean_screen; /* TRUE if the whole screen will be cleared */
84
 
  gint clear_timeout_id; /* timeout for redrawing */
85
 
  gboolean needs_cleaning; /* FALSE for XVideo and chromakey */
 
72
  GdkWindow *           root_window;
 
73
 
 
74
  GtkWidget *           main_window;    /* top level parent of video_window */
 
75
  GtkWidget *           video_window;   /* displays the overlay */
 
76
 
 
77
  const tv_screen *     screen;         /* screen containing video_window
 
78
                                           (Xinerama) */
 
79
 
 
80
  gint                  mw_x;           /* last known main_window position */
 
81
  gint                  mw_y;           /* (root relative) */
 
82
 
 
83
  gint                  vw_x;           /* last known video_window position */
 
84
  gint                  vw_y;           /* (main_window relative) */
 
85
 
 
86
  guint                 vw_width;       /* last known video_window size */
 
87
  guint                 vw_height;
 
88
 
 
89
  gboolean              needs_cleaning; /* FALSE for XVideo and chromakey */
 
90
 
 
91
  tveng_device_info *   info;           /* V4L device */
 
92
 
 
93
  /* DMA overlay */
 
94
 
 
95
  GdkVisibilityState    visibility;     /* of video_window */
 
96
 
 
97
  tv_window             window;         /* overlay rectangle */
 
98
 
 
99
  tv_clip_vector        cur_vector;
 
100
  tv_clip_vector        tmp_vector;
 
101
  tv_clip_vector        set_vector;
 
102
 
 
103
  gboolean              clean_screen;
 
104
  gboolean              geometry_changed;
 
105
 
 
106
  guint                 timeout_id;
86
107
} tv_info;
87
108
 
88
 
/* Pointer to a gdk wrapper for the root window */
89
 
static GdkWindow        *root_window = NULL;
90
 
 
91
 
/*
92
 
 * Just like x11_get_clips, but fixes the dword-align ugliness
93
 
 */
94
 
static struct tveng_clip *
95
 
overlay_get_clips(GdkWindow * window, gint * clipcount)
96
 
{
97
 
  struct tveng_clip * clips;
98
 
 
99
 
  g_assert(clipcount != NULL);
100
 
 
101
 
  if (!window || !tv_info.needs_cleaning)
102
 
    {
103
 
      *clipcount = 0;
104
 
      return NULL;
105
 
    }
106
 
 
107
 
  clips = x11_get_clips(window,
108
 
                        tv_info.info->window.x,
109
 
                        tv_info.info->window.y,
110
 
                        tv_info.info->window.width,
111
 
                        tv_info.info->window.height,
112
 
                        clipcount);
113
 
 
114
 
  return clips;
115
 
}
116
 
 
117
 
/*
118
 
 * Checks whether two clipping structs look the same.
119
 
 */
120
 
static gboolean
121
 
compare_clips(struct tveng_clip *a, gint na, struct tveng_clip* b,
122
 
              gint nb)
123
 
{
124
 
  gint i;
125
 
 
126
 
  if (na != nb)
127
 
    return FALSE;
128
 
 
129
 
  for (i = 0; i < na; i++)
130
 
    if ((a[i].x != b[i].x) || (a[i].y != b[i].y) ||
131
 
        (a[i].width != b[i].width) || (a[i].height != b[i].height))
132
 
      return FALSE;
133
 
 
134
 
  return TRUE;
135
 
}
136
 
 
137
 
/*
138
 
 * Clearing needed callback
139
 
 */
140
 
static gint
141
 
overlay_clearing_timeout(gpointer data)
142
 
{
143
 
  struct tveng_clip * clips;
144
 
  gint clipcount;
145
 
 
146
 
  clips = overlay_get_clips(tv_info.window->window, &clipcount);
147
 
 
148
 
  if (!compare_clips(clips, clipcount, tv_info.clips,
149
 
                     tv_info.clipcount))
150
 
    { /* delay the update till the situation stabilizes */
151
 
      if (tv_info.clips)
152
 
        g_free(tv_info.clips);
153
 
 
154
 
      tv_info.clips = clips;
155
 
      tv_info.clipcount = clipcount;
156
 
      tv_info.clean_screen = TRUE;
157
 
        
158
 
      return TRUE; /* call me again */
159
 
    }
160
 
 
161
 
  if (clips)
162
 
    g_free(clips);
163
 
 
164
 
  if ((tv_info.info->current_mode == TVENG_CAPTURE_WINDOW) &&
165
 
      (tv_info.needs_cleaning) && (tv_info.visible))
166
 
    {
 
109
static __inline__ tv_bool
 
110
tv_window_equal                 (const tv_window *      window1,
 
111
                                 const tv_window *      window2)
 
112
{
 
113
  return (0 == ((window1->x ^ window2->x) |
 
114
                (window1->y ^ window2->y) |
 
115
                (window1->width ^ window2->width) |
 
116
                (window1->height ^ window2->height)));
 
117
}
 
118
 
 
119
static void
 
120
get_clips                       (tv_clip_vector *       vector)
 
121
{
 
122
  if (!x11_window_clip_vector
 
123
      (vector,
 
124
       GDK_WINDOW_XDISPLAY (tv_info.video_window->window),
 
125
       GDK_WINDOW_XID (tv_info.video_window->window),
 
126
       tv_info.window.x,
 
127
       tv_info.window.y,
 
128
       tv_info.window.width,
 
129
       tv_info.window.height))
 
130
    g_assert_not_reached ();
 
131
 
 
132
  if (OVERLAY_DUMP_CLIPS)
 
133
    {
 
134
      tv_clip *clip;
 
135
      unsigned int i;
 
136
 
 
137
      clip = vector->vector;
 
138
 
 
139
      fprintf (stderr, "get_clips %u:\n", vector->size);
 
140
 
 
141
      for (i = 0; i < vector->size; ++i, ++clip)
 
142
        fprintf (stderr, "%3u: %3u, %3u - %3u, %3u\n",
 
143
                 i, clip->x1, clip->y1, clip->x2, clip->y2);
 
144
    }
 
145
}
 
146
 
 
147
static void
 
148
expose_window_clip_vector       (const tv_window *      window,
 
149
                                 const tv_clip_vector * clip_vector)
 
150
{
 
151
  tv_clip *clip;
 
152
  guint count;
 
153
 
 
154
  clip = clip_vector->vector;
 
155
  count = clip_vector->size;
 
156
 
 
157
  while (count-- > 0)
 
158
    {
 
159
      x11_force_expose (window->x + clip->x1,
 
160
                        window->y + clip->y1,
 
161
                        (guint)(clip->x2 - clip->x1),
 
162
                        (guint)(clip->y2 - clip->y1));
 
163
      ++clip;
 
164
    }
 
165
}
 
166
 
 
167
static void
 
168
expose_screen                   (void)
 
169
{
 
170
  tv_info.clean_screen = FALSE;
 
171
 
 
172
  x11_force_expose ((gint) tv_info.screen->x,
 
173
                    (gint) tv_info.screen->y,
 
174
                    tv_info.screen->width,
 
175
                    tv_info.screen->height);
 
176
}
 
177
 
 
178
static gboolean
 
179
set_window                      (void)
 
180
{
 
181
  if (tv_set_overlay_window_clipvec (tv_info.info,
 
182
                                     &tv_info.window,
 
183
                                     &tv_info.cur_vector))
 
184
    {
 
185
      tv_clip_vector_set (&tv_info.set_vector, &tv_info.cur_vector);
 
186
      return TRUE;
 
187
    }
 
188
 
 
189
  return FALSE;
 
190
}
 
191
 
 
192
static gboolean
 
193
obscured_timeout                (gpointer               user_data _unused_)
 
194
{
 
195
  const tv_window *window;
 
196
 
 
197
  if (OVERLAY_LOG_FP)
 
198
    fprintf (OVERLAY_LOG_FP, "obscured_timeout\n");
 
199
 
 
200
  /* Changed from partially visible or unobscured to fully obscured. */
 
201
 
 
202
  window = tv_cur_overlay_window (tv_info.info);
 
203
 
 
204
  if (tv_info.clean_screen)
 
205
    expose_screen ();
 
206
  else
 
207
    x11_force_expose (window->x,
 
208
                      window->y,
 
209
                      window->width,
 
210
                      window->height);
 
211
 
 
212
  tv_clip_vector_clear (&tv_info.cur_vector);
 
213
  /* XXX error ignored */
 
214
  tv_clip_vector_add_clip_xy (&tv_info.cur_vector,
 
215
                              0, 0, window->width, window->height);
 
216
 
 
217
  tv_info.geometry_changed = TRUE;
 
218
 
 
219
  tv_info.timeout_id = NO_SOURCE_ID;
 
220
  return FALSE; /* remove timeout */
 
221
}
 
222
 
 
223
static gboolean
 
224
visible_timeout                 (gpointer               user_data _unused_)
 
225
{
 
226
  if (OVERLAY_LOG_FP)
 
227
    fprintf (OVERLAY_LOG_FP, "visible_timeout\n");
 
228
 
 
229
  /* Changed from
 
230
     a) fully obscured or partially visible to unobscured
 
231
        (cleaning the clip vector is not enough, we must
 
232
         still clip against the video_window bounds)
 
233
     b) fully obscured or unobscured to partially visible
 
234
     c) possible clip vector change while partially obscured
 
235
  */
 
236
 
 
237
  /* XXX error */
 
238
  get_clips (&tv_info.tmp_vector);
 
239
 
 
240
  if (!tv_clip_vector_equal (&tv_info.cur_vector,
 
241
                             &tv_info.tmp_vector))
 
242
    {
 
243
      /* Delay until the situation stabilizes. */
 
244
 
 
245
      if (OVERLAY_LOG_FP)
 
246
        fprintf (OVERLAY_LOG_FP, "visible_timeout: delay\n");
 
247
 
 
248
      SWAP (tv_info.cur_vector, tv_info.tmp_vector);
 
249
 
 
250
      tv_info.geometry_changed = TRUE;
 
251
 
 
252
      return TRUE; /* call again */
 
253
    }
 
254
 
 
255
  /* Resume overlay. */
 
256
 
 
257
  if (tv_info.geometry_changed)
 
258
    {
 
259
      const tv_screen *xs;
 
260
      guint retry_count;
 
261
 
 
262
      tv_info.geometry_changed = FALSE;
 
263
 
 
264
      if (OVERLAY_LOG_FP)
 
265
        fprintf (OVERLAY_LOG_FP, "visible_timeout: geometry change\n");
 
266
 
 
267
      xs = tv_screen_list_find (screens,
 
268
                                    tv_info.mw_x + tv_info.vw_x,
 
269
                                    tv_info.mw_y + tv_info.vw_y,
 
270
                                    (guint) tv_info.vw_width,
 
271
                                    (guint) tv_info.vw_height);
 
272
      if (!xs)
 
273
        xs = screens;
 
274
 
 
275
      if (tv_info.screen != xs)
 
276
        {
 
277
          /* Moved to other screen (Xinerama). */
 
278
 
 
279
          tv_info.screen = xs;
 
280
          if (!z_set_overlay_buffer (tv_info.info,
 
281
                                     tv_info.screen,
 
282
                                     tv_info.video_window->window))
 
283
            goto finish; /* XXX */
 
284
        }
 
285
 
 
286
      /* Other windows may have received expose events before we
 
287
         were able to turn off the overlay. Resend expose
 
288
         events for those regions which should be clean now. */
167
289
      if (tv_info.clean_screen)
168
 
        x11_force_expose(0, 0, gdk_screen_width(), gdk_screen_height());
169
 
      else
170
 
        x11_force_expose(tv_info.info->window.x-40,
171
 
                         tv_info.info->window.y-40,
172
 
                         tv_info.info->window.width+80,
173
 
                         tv_info.info->window.height+80);
174
 
 
175
 
      tv_info.clean_screen = FALSE;
176
 
    }
177
 
 
178
 
  /* Setup the overlay and start it again if needed */
179
 
  if (tv_info.info->current_mode == TVENG_CAPTURE_WINDOW)
180
 
    {
181
 
      overlay_sync(FALSE);
182
 
      if (tv_info.needs_cleaning)
183
 
        {
184
 
          if (tv_info.visible)
185
 
            tveng_set_preview_on(tv_info.info);
186
 
          else
187
 
            tveng_set_preview_off(tv_info.info);
188
 
        }
189
 
    }
190
 
 
191
 
  *((gint*)data) = -1; /* set timeout_id to destroyed */
192
 
 
193
 
  return FALSE; /* the timeout will be destroyed */
194
 
}
195
 
 
196
 
/*
197
 
 * Something has changed, schedule a redraw in TIMEOUT ms
198
 
 * clean_screen: the whole screen needs updating
199
 
 */
200
 
static void
201
 
overlay_status_changed(gboolean clean_screen)
202
 
{
203
 
  if (tv_info.clear_timeout_id >= 0)
204
 
    gtk_timeout_remove(tv_info.clear_timeout_id);
205
 
 
206
 
  tv_info.clear_timeout_id =
207
 
    gtk_timeout_add(CLEAR_TIMEOUT, overlay_clearing_timeout,
208
 
                    &(tv_info.clear_timeout_id));
209
 
 
210
 
  if ((tv_info.info->current_mode == TVENG_CAPTURE_WINDOW) &&
211
 
      (tv_info.needs_cleaning))
212
 
    tveng_set_preview_off(tv_info.info);
213
 
 
214
 
  if (!tv_info.clean_screen)
215
 
    tv_info.clean_screen = clean_screen;
216
 
}
217
 
 
218
 
/*
219
 
 * event handler for the toplevel overlay window
220
 
 */
221
 
static gboolean
222
 
on_main_overlay_event        (GtkWidget       *widget,
223
 
                              GdkEvent        *event,
224
 
                              gpointer         user_data)
225
 
{
226
 
  switch (event->type)
227
 
    {
 
290
        expose_screen ();
 
291
      else
 
292
        expose_window_clip_vector (tv_cur_overlay_window (tv_info.info),
 
293
                                   &tv_info.set_vector);
 
294
 
 
295
      /* Desired overlay bounds */
 
296
 
 
297
      tv_info.window.x          = tv_info.mw_x + tv_info.vw_x;
 
298
      tv_info.window.y          = tv_info.mw_y + tv_info.vw_y;
 
299
      tv_info.window.width      = tv_info.vw_width;
 
300
      tv_info.window.height     = tv_info.vw_height;
 
301
 
 
302
      retry_count = 5;
 
303
 
 
304
      for (;;)
 
305
        {
 
306
          const tv_window *w;
 
307
 
 
308
          if (retry_count-- == 0)
 
309
            goto finish; /* XXX */
 
310
 
 
311
          /* XXX error */
 
312
          set_window ();
 
313
 
 
314
          w = tv_cur_overlay_window (tv_info.info);
 
315
 
 
316
          if (tv_window_equal (&tv_info.window, w))
 
317
            break;
 
318
 
 
319
          /* The driver modified the overlay bounds (alignment, limits),
 
320
             we must update the clips. */
 
321
 
 
322
          tv_info.window.x      = w->x;
 
323
          tv_info.window.y      = w->y;
 
324
          tv_info.window.width  = w->width;
 
325
          tv_info.window.height = w->height;
 
326
 
 
327
          /* XXX error */
 
328
          get_clips (&tv_info.tmp_vector);
 
329
        }
 
330
    }
 
331
  else if (tv_info.clean_screen)
 
332
    {
 
333
      expose_screen ();
 
334
    }
 
335
 
 
336
  if (!OVERLAY_CHROMA_TEST)
 
337
    {
 
338
      /* XXX error ignored */
 
339
      tv_enable_overlay (tv_info.info, TRUE);
 
340
    }
 
341
 
 
342
 finish:
 
343
  tv_info.timeout_id = NO_SOURCE_ID;
 
344
  return FALSE; /* remove timeout */
 
345
}
 
346
 
 
347
static void
 
348
stop_timeout                    (void)
 
349
{
 
350
  if (tv_info.timeout_id > 0)
 
351
    g_source_remove (tv_info.timeout_id);
 
352
 
 
353
  tv_info.timeout_id = NO_SOURCE_ID;
 
354
}
 
355
 
 
356
static void
 
357
restart_timeout                 (void)
 
358
{
 
359
  if (OVERLAY_LOG_FP)
 
360
    fprintf (OVERLAY_LOG_FP, "restart_timeout\n");
 
361
 
 
362
  tv_enable_overlay (tv_info.info, FALSE);
 
363
 
 
364
  stop_timeout ();
 
365
 
 
366
  if (tv_info.visibility == GDK_VISIBILITY_FULLY_OBSCURED)
 
367
    tv_info.timeout_id =
 
368
      g_timeout_add (CLEAR_TIMEOUT,
 
369
                     (GSourceFunc) obscured_timeout, &tv_info);
 
370
  else
 
371
    tv_info.timeout_id =
 
372
      g_timeout_add (CLEAR_TIMEOUT,
 
373
                     (GSourceFunc) visible_timeout, &tv_info);
 
374
}
 
375
 
 
376
static __inline__ void
 
377
reconfigure                     (void)
 
378
{
 
379
  tv_info.geometry_changed = TRUE;
 
380
  tv_info.clean_screen = TRUE; /* see root_filter() below */
 
381
 
 
382
  if (tv_info.visibility != GDK_VISIBILITY_FULLY_OBSCURED)
 
383
    restart_timeout ();
 
384
}
 
385
 
 
386
static gboolean
 
387
on_video_window_event           (GtkWidget *            widget _unused_,
 
388
                                 GdkEvent *             event,
 
389
                                 gpointer               user_data _unused_)
 
390
{
 
391
  if (OVERLAY_EVENT_LOG_FP)
 
392
    fprintf (OVERLAY_EVENT_LOG_FP, "on_video_window_event: GDK_%s\n",
 
393
             z_gdk_event_name (event));
 
394
 
 
395
  switch (event->type)
 
396
    {
 
397
    case GDK_CONFIGURE:
 
398
      /* Size, position, stacking order changed. Note position
 
399
         is relative to the parent window. */
 
400
 
 
401
      if (tv_info.needs_cleaning)
 
402
        {
 
403
          if (tv_info.vw_width != (guint) event->configure.width
 
404
              || tv_info.vw_height != (guint) event->configure.height
 
405
              || tv_info.vw_x != (gint) event->configure.x
 
406
              || tv_info.vw_y != (gint) event->configure.y)
 
407
            {
 
408
              tv_info.vw_x = event->configure.x; /* XXX really mw relative? */
 
409
              tv_info.vw_y = event->configure.y;
 
410
              
 
411
              tv_info.vw_width = event->configure.width;
 
412
              tv_info.vw_height = event->configure.height;
 
413
 
 
414
              reconfigure ();
 
415
            }
 
416
        }
 
417
      else
 
418
        {
 
419
          /* XVideo overlay is automatically positioned relative to
 
420
             the video_window origin, only have to adjust size. */
 
421
 
 
422
          tv_enable_overlay (tv_info.info, FALSE);
 
423
 
 
424
          /* XXX tveng_set_preview_window (currently default is
 
425
             fill window size) */
 
426
 
 
427
          if (!OVERLAY_CHROMA_TEST)
 
428
            {
 
429
              /* XXX error
 
430
                 XXX off/on is inefficient, XVideo does this automatically. */
 
431
              tv_enable_overlay (tv_info.info, TRUE);
 
432
            }
 
433
        }
 
434
 
 
435
      break;
 
436
 
 
437
    case GDK_VISIBILITY_NOTIFY:
 
438
      /* Visibility state changed: obscured, partially or fully visible. */
 
439
 
 
440
      if (!tv_info.needs_cleaning)
 
441
        break;
 
442
 
 
443
      tv_info.visibility = event->visibility.state;
 
444
      restart_timeout ();
 
445
      break;
 
446
 
 
447
    case GDK_EXPOSE:
 
448
      /* Parts of the video window have been exposed, e.g. menu window has
 
449
         been closed. Remove those clips. */
 
450
 
 
451
      if (OVERLAY_EVENT_LOG_FP)
 
452
        fprintf (OVERLAY_EVENT_LOG_FP, "Expose rect %d,%d - %d,%d\n",
 
453
                 event->expose.area.x,
 
454
                 event->expose.area.y,
 
455
                 event->expose.area.x + event->expose.area.width - 1,
 
456
                 event->expose.area.y + event->expose.area.height - 1);
 
457
 
 
458
      if (tv_info.needs_cleaning)
 
459
        {
 
460
          tv_info.geometry_changed = TRUE;
 
461
          restart_timeout ();
 
462
        }
 
463
 
 
464
      break;
 
465
 
 
466
    default:
 
467
      break;
 
468
    }
 
469
 
 
470
  return FALSE; /* pass on */
 
471
}
 
472
 
 
473
static gboolean
 
474
on_main_window_event            (GtkWidget *            widget _unused_,
 
475
                                 GdkEvent *             event,
 
476
                                 gpointer               user_data _unused_)
 
477
{
 
478
  if (OVERLAY_EVENT_LOG_FP)
 
479
    fprintf (OVERLAY_EVENT_LOG_FP, "on_main_window_event: GDK_%s\n",
 
480
             z_gdk_event_name (event));
 
481
 
 
482
  switch (event->type)
 
483
    {
 
484
    case GDK_CONFIGURE:
 
485
      /* Size, position, stacking order changed. Note position
 
486
         is relative to the parent window. */
 
487
 
 
488
      if (tv_info.mw_x != event->configure.x
 
489
          || tv_info.mw_y != event->configure.y)
 
490
        {
 
491
          tv_info.mw_x = event->configure.x; /* XXX really root relative? */
 
492
          tv_info.mw_y = event->configure.y;
 
493
 
 
494
          reconfigure ();
 
495
        }
 
496
 
 
497
      break;
 
498
 
228
499
    case GDK_UNMAP:
229
 
      tv_info.visible = FALSE;
230
 
      overlay_sync(TRUE);
231
 
      overlay_status_changed(TRUE);
232
 
      break;
233
 
    case GDK_MAP:
234
 
      tv_info.visible = TRUE;
235
 
      overlay_status_changed(FALSE);
236
 
      break;
237
 
    case GDK_CONFIGURE:
238
 
      overlay_status_changed(TRUE);
239
 
      break;
 
500
      /* Window was rolled up or minimized. No visibility events are
 
501
         sent in this case. */
 
502
 
 
503
      tv_info.visibility = GDK_VISIBILITY_FULLY_OBSCURED;
 
504
      restart_timeout ();
 
505
      break;
 
506
 
240
507
    default:
241
508
      break;
242
509
    }
243
510
 
244
 
  return FALSE;
 
511
  return FALSE; /* pass on */
245
512
}
246
513
 
247
 
/**
248
 
 * Event handler for the root window.
249
 
 */
250
514
static GdkFilterReturn
251
 
x_root_filter           (GdkXEvent      *xev,
252
 
                         GdkEvent       *event,
253
 
                         gpointer       data)
 
515
root_filter                     (GdkXEvent *            gdkxevent,
 
516
                                 GdkEvent *             unused _unused_,
 
517
                                 gpointer               data _unused_)
254
518
{
255
 
  struct tveng_clip * clips;
256
 
  gint clipcount;
257
 
 
258
 
  if (tv_info.clear_timeout_id >= 0 || !tv_info.needs_cleaning ||
259
 
      !tv_info.visible)
260
 
    return GDK_FILTER_CONTINUE; /* no need for this */
261
 
 
262
 
  clips = overlay_get_clips(tv_info.window->window, &clipcount);
263
 
 
264
 
  if (!compare_clips(clips, clipcount, tv_info.clips,
265
 
                     tv_info.clipcount))
266
 
    overlay_status_changed(TRUE);
267
 
 
268
 
  g_free(clips);
 
519
  XEvent *event = (XEvent *) gdkxevent;
 
520
 
 
521
  if (OVERLAY_EVENT_LOG_FP)
 
522
    {
 
523
      fprintf (OVERLAY_EVENT_LOG_FP, "root_filter: ");
 
524
 
 
525
      switch (event->type)
 
526
        {
 
527
        case MotionNotify:      fprintf (OVERLAY_EVENT_LOG_FP, "MotionNotify\n"); break;
 
528
        case Expose:            fprintf (OVERLAY_EVENT_LOG_FP, "Expose\n"); break;
 
529
        case VisibilityNotify:  fprintf (OVERLAY_EVENT_LOG_FP, "VisibilityNotify\n"); break;
 
530
        case CreateNotify:      fprintf (OVERLAY_EVENT_LOG_FP, "CreateNotify\n"); break;
 
531
        case DestroyNotify:     fprintf (OVERLAY_EVENT_LOG_FP, "DestroyNotify\n"); break;
 
532
        case UnmapNotify:       fprintf (OVERLAY_EVENT_LOG_FP, "UnmapNotify\n"); break;
 
533
        case MapNotify:         fprintf (OVERLAY_EVENT_LOG_FP, "MapNotify\n"); break;
 
534
        case ConfigureNotify:   fprintf (OVERLAY_EVENT_LOG_FP, "ConfigureNotify\n"); break;
 
535
        case GravityNotify:     fprintf (OVERLAY_EVENT_LOG_FP, "GravityNotify\n"); break;
 
536
        default:                fprintf (OVERLAY_EVENT_LOG_FP, "Unknown\n"); break;
 
537
        }
 
538
    }
 
539
 
 
540
  if (tv_info.visibility != GDK_VISIBILITY_PARTIAL)
 
541
    return GDK_FILTER_CONTINUE;
 
542
 
 
543
  switch (event->type)
 
544
    {
 
545
    case ConfigureNotify:
 
546
      {
 
547
        /* We could just refresh regions we previously DMAed, but unfortunately
 
548
           it's possible to move a destroyed window away before we did. What's
 
549
           worse, imperfect refresh or unconditionally refreshing the entire
 
550
           screen? */
 
551
 
 
552
        if (1)
 
553
          {
 
554
            tv_info.clean_screen = TRUE;
 
555
          }
 
556
        else
 
557
          {
 
558
            XConfigureEvent *ev = &event->xconfigure;
 
559
            const tv_window *win = tv_cur_overlay_window (tv_info.info);
 
560
 
 
561
            if ((int)(ev->x - ev->border_width) >= (int)(win->x + win->width)
 
562
                || (int)(ev->x + ev->width + ev->border_width) <= (int) win->x
 
563
                || (int)(ev->y - ev->border_width) >= (int)(win->y + win->height)
 
564
                || (int)(ev->y + ev->height + ev->border_width) <= (int) win->y)
 
565
              /* Windows do not overlap. */
 
566
              return GDK_FILTER_CONTINUE;
 
567
          }
 
568
 
 
569
        restart_timeout ();
 
570
 
 
571
        break;
 
572
      }
 
573
 
 
574
    default:
 
575
      break;
 
576
    }
269
577
 
270
578
  return GDK_FILTER_CONTINUE;
271
579
}
272
580
 
273
 
/*
274
 
 * Called when the tv_screen geometry changes
275
 
 */
 
581
/* The osd pieces have changed, avoid flicker if not needed. */
276
582
static void
277
 
on_tv_screen_size_allocate             (GtkWidget       *widget,
278
 
                                        GtkAllocation   *allocation,
279
 
                                        gpointer        ignored)
 
583
on_osd_model_changed            (ZModel *               osd_model _unused_,
 
584
                                 gpointer               ignored _unused_)
280
585
{
281
 
  static gint old_w=-1, old_h;
282
 
 
283
 
  /**
284
 
   * GtkFixed sends allocation events when removing
285
 
   * children from it, even when no resize is involved. Ignore these.
286
 
   */
287
 
  if (old_w == allocation->width &&
288
 
      old_h == allocation->height)
 
586
  if (tv_info.visibility == GDK_VISIBILITY_FULLY_OBSCURED)
289
587
    return;
290
588
 
291
 
  old_w = allocation->width;
292
 
  old_h = allocation->height;
293
 
 
294
 
  overlay_status_changed(FALSE);
295
 
}
296
 
 
297
 
/*
298
 
 * Called when the main overlay window is destroyed (shuts down the
299
 
 * timers)
300
 
 */
 
589
  tv_info.visibility = GDK_VISIBILITY_PARTIAL;
 
590
  restart_timeout ();
 
591
}
 
592
 
 
593
static void
 
594
terminate                       (void)
 
595
{
 
596
  stop_timeout ();
 
597
 
 
598
  tv_enable_overlay (tv_info.info, FALSE);
 
599
 
 
600
  if (tv_info.needs_cleaning)
 
601
    {
 
602
      usleep (CLEAR_TIMEOUT * 1000);
 
603
 
 
604
      if (tv_info.clean_screen)
 
605
        expose_screen ();
 
606
      else
 
607
        expose_window_clip_vector (tv_cur_overlay_window (tv_info.info),
 
608
                                   &tv_info.set_vector);
 
609
    }
 
610
}
 
611
 
301
612
static gboolean
302
 
on_main_overlay_delete_event           (GtkWidget       *widget,
303
 
                                        GdkEvent        *event,
304
 
                                        gpointer         user_data)
305
 
{
306
 
  if (tv_info.clear_timeout_id >= 0)
307
 
    {
308
 
      gtk_timeout_remove(tv_info.clear_timeout_id);
309
 
      tv_info.clear_timeout_id = -1;
310
 
    }
311
 
 
312
 
  return FALSE; /* go on with the callbacks */
313
 
}
314
 
 
315
 
/*
316
 
 * The osd pieces have changed, avoid flicker if not needed.
317
 
 */
318
 
static void
319
 
on_osd_model_changed                    (ZModel         *osd_model,
320
 
                                         gpointer       ignored)
321
 
{
322
 
  struct tveng_window window;
323
 
 
324
 
  if (tv_info.clear_timeout_id >= 0 || !tv_info.needs_cleaning ||
325
 
      !tv_info.visible)
326
 
    return; /* no need for this */
327
 
 
328
 
  if (tv_info.clips)
329
 
    g_free(tv_info.clips);
330
 
 
331
 
  tv_info.clips =
332
 
    overlay_get_clips(tv_info.window->window, &tv_info.clipcount);
333
 
 
334
 
  memcpy(&window, &tv_info.info->window, sizeof(struct tveng_window));
335
 
  tveng_set_preview_off(tv_info.info);
336
 
  memcpy(&tv_info.info->window, &window, sizeof(struct tveng_window));
337
 
  tv_info.info->window.clips = tv_info.clips;
338
 
  tv_info.info->window.clipcount = tv_info.clipcount;
339
 
  tveng_set_preview_window(tv_info.info);
340
 
  tveng_set_preview_on(tv_info.info);
341
 
}
342
 
 
343
 
/*
344
 
 * Inits the overlay engine.
345
 
 * window: The window we will be overlaying to.
346
 
 * main_window: The toplevel window window belongs to. They can be the
347
 
 * same.
348
 
 */
349
 
void
350
 
startup_overlay(GtkWidget * window, GtkWidget * main_window,
351
 
                tveng_device_info * info)
352
 
{
353
 
  GdkEventMask mask; /* The GDK events we want to see */
354
 
  GdkColor chroma = {0, 0, 0, 0};
355
 
 
356
 
  gtk_signal_connect(GTK_OBJECT(main_window), "event",
357
 
                     GTK_SIGNAL_FUNC(on_main_overlay_event),
358
 
                     NULL);
359
 
 
360
 
  gtk_signal_connect(GTK_OBJECT(main_window), "delete-event",
361
 
                     GTK_SIGNAL_FUNC(on_main_overlay_delete_event),
362
 
                     NULL);
363
 
 
364
 
  gtk_signal_connect(GTK_OBJECT(window), "size-allocate",
365
 
                     GTK_SIGNAL_FUNC(on_tv_screen_size_allocate),
366
 
                     NULL);
367
 
 
368
 
  /*
369
 
    gdk has no [Sub]structureNotify wrapper, but a timer will
370
 
    do nicely.
371
 
  */
372
 
  mask = gdk_window_get_events(window->window);
373
 
  mask |= (GDK_EXPOSURE_MASK | GDK_VISIBILITY_NOTIFY_MASK);
374
 
  gdk_window_set_events(window->window, mask);
375
 
 
376
 
  mask = gdk_window_get_events(main_window->window);
377
 
  mask |= (GDK_EXPOSURE_MASK | GDK_VISIBILITY_NOTIFY_MASK);
378
 
  gdk_window_set_events(main_window->window, mask);
379
 
 
380
 
  tv_info.window = window;
381
 
  tv_info.main_window = main_window;
382
 
  tv_info.info = info;
383
 
  tv_info.visible = x11_window_viewable(window->window);
384
 
 
385
 
  tv_info.clear_timeout_id = -1;
386
 
  tv_info.clean_screen = FALSE;
387
 
  tv_info.clips = NULL;
388
 
  tv_info.clipcount = 0;
389
 
  if (info->current_controller == TVENG_CONTROLLER_XV)
390
 
    tv_info.needs_cleaning = FALSE;
391
 
  else
392
 
    tv_info.needs_cleaning = TRUE;
393
 
  gdk_window_get_size(window->window, &tv_info.w, &tv_info.h);
394
 
  gdk_window_get_origin(window->window, &tv_info.x, &tv_info.y);
395
 
 
396
 
  if (info->current_controller != TVENG_CONTROLLER_XV)
397
 
    {
398
 
      if (info->caps.flags & TVENG_CAPS_CHROMAKEY)
399
 
        {
400
 
          chroma.red = chroma.green = 0;
401
 
          chroma.blue = 0xffff;
402
 
          
403
 
          if (gdk_colormap_alloc_color(gdk_colormap_get_system(), &chroma,
404
 
                                       FALSE, TRUE))
405
 
            tveng_set_chromakey(chroma.red >> 8, chroma.green >> 8,
406
 
                                chroma.blue >> 8, info);
407
 
          else
408
 
            ShowBox("Couldn't allocate chromakey, chroma won't work",
409
 
                    GNOME_MESSAGE_BOX_WARNING);
410
 
        }
411
 
 
412
 
      gdk_window_set_background(window->window, &chroma);
413
 
 
414
 
      if (chroma.pixel != 0)
415
 
        gdk_colormap_free_colors(gdk_colormap_get_system(), &chroma,
416
 
                                 1);
417
 
    }
418
 
  else
419
 
    {
420
 
      gdk_window_set_background(window->window, &chroma);
421
 
    }
422
 
 
423
 
#ifdef HAVE_LIBZVBI
424
 
  if (tv_info.needs_cleaning)
425
 
    {
426
 
      if (!root_window)
427
 
        {
428
 
          root_window = gdk_window_foreign_new(GDK_ROOT_WINDOW());
429
 
          gdk_window_set_events(root_window,
430
 
                                GDK_STRUCTURE_MASK |
431
 
                                GDK_SUBSTRUCTURE_MASK);
432
 
        }
433
 
      gdk_window_add_filter(root_window, x_root_filter, NULL);
434
 
 
435
 
      gtk_signal_connect(GTK_OBJECT(osd_model), "changed",
436
 
                         GTK_SIGNAL_FUNC(on_osd_model_changed),
437
 
                         NULL);
438
 
      /* Update the cliplist now */
439
 
      on_osd_model_changed(osd_model, NULL);
440
 
    }
441
 
#endif /* HAVE_LIBZVBI */
442
 
}
443
 
 
444
 
/*
445
 
 * Stops the overlay engine.
446
 
 */
447
 
void
448
 
overlay_stop(tveng_device_info *info)
449
 
{
450
 
  gtk_signal_disconnect_by_func(GTK_OBJECT(tv_info.main_window),
451
 
                                GTK_SIGNAL_FUNC(on_main_overlay_event),
452
 
                                NULL);
453
 
 
454
 
  gtk_signal_disconnect_by_func(GTK_OBJECT(tv_info.main_window),
455
 
                                GTK_SIGNAL_FUNC(on_main_overlay_delete_event),
456
 
                                NULL);
457
 
 
458
 
  gtk_signal_disconnect_by_func(GTK_OBJECT(tv_info.window),
459
 
                                GTK_SIGNAL_FUNC(on_tv_screen_size_allocate),
460
 
                                NULL);
461
 
#ifdef HAVE_LIBZVBI
462
 
  if (tv_info.needs_cleaning)
463
 
    gtk_signal_disconnect_by_func(GTK_OBJECT(osd_model),
464
 
                                  GTK_SIGNAL_FUNC(on_osd_model_changed),
465
 
                                  NULL);
466
 
#endif
467
 
 
468
 
  if (tv_info.needs_cleaning)
469
 
    gdk_window_remove_filter(root_window, x_root_filter, NULL);
470
 
 
471
 
  if (tv_info.clear_timeout_id >= 0)
472
 
    {
473
 
      gtk_timeout_remove(tv_info.clear_timeout_id);
474
 
      tv_info.clear_timeout_id = -1;
475
 
    }
476
 
 
477
 
  if (tv_info.needs_cleaning)
478
 
    x11_force_expose(0, 0, gdk_screen_width(), gdk_screen_height());
479
 
}
480
 
 
481
 
/*
482
 
 * Shuts down the overlay engine
483
 
 */
484
 
void
485
 
shutdown_overlay(void )
486
 
{
487
 
  /* Nothing to be done */
488
 
}
489
 
 
490
 
/*
491
 
 * Tells the overlay engine to sync the overlay with the window.
492
 
 * clean_screen: TRUE means that we should refresh the whole screeen.
493
 
 */
494
 
void
495
 
overlay_sync(gboolean clean_screen)
496
 
{
497
 
  extern tveng_device_info *main_info;
498
 
 
499
 
  if (main_info->current_mode != TVENG_CAPTURE_WINDOW)
500
 
    return;
501
 
 
502
 
  gdk_window_get_size(tv_info.window->window, &tv_info.w, &tv_info.h);
503
 
  gdk_window_get_origin(tv_info.window->window, &tv_info.x,
504
 
                        &tv_info.y);
505
 
 
506
 
  if (tv_info.clips)
507
 
    g_free(tv_info.clips);
508
 
 
509
 
  tv_info.info->window.x = tv_info.x;
510
 
  tv_info.info->window.y = tv_info.y;
511
 
  tv_info.info->window.width = tv_info.w;
512
 
  tv_info.info->window.height = tv_info.h;
513
 
  tv_info.info->window.clips = tv_info.clips =
514
 
    overlay_get_clips(tv_info.window->window,
515
 
                      &(tv_info.info->window.clipcount));
516
 
  tv_info.clipcount = tv_info.info->window.clipcount;
517
 
  tv_info.info->window.win = GDK_WINDOW_XWINDOW(tv_info.window->window);
518
 
  tv_info.info->window.gc = GDK_GC_XGC(tv_info.window->style->white_gc);
519
 
 
520
 
  tveng_set_preview_window(tv_info.info);
521
 
 
522
 
  if (tv_info.clips) {
523
 
    g_free(tv_info.clips);
524
 
    tv_info.clips = NULL;
525
 
  }
526
 
 
527
 
  /* The requested overlay coords might not be the definitive ones,
528
 
     adapt the clips */
529
 
  if (tv_info.needs_cleaning)
530
 
    {
531
 
      tv_info.clips = tv_info.info->window.clips =
532
 
        overlay_get_clips(tv_info.window->window, &(tv_info.clipcount));
533
 
      tv_info.info->window.clipcount = tv_info.clipcount;
534
 
      
535
 
      tveng_set_preview_window(tv_info.info);
536
 
      
537
 
      if (clean_screen)
538
 
        x11_force_expose(0, 0, gdk_screen_width(), gdk_screen_height());
539
 
    }
 
613
on_main_window_delete_event     (GtkWidget *            widget _unused_,
 
614
                                 GdkEvent *             event _unused_,
 
615
                                 gpointer               user_data _unused_)
 
616
{
 
617
  terminate ();
 
618
 
 
619
  return FALSE; /* pass on */
 
620
}
 
621
 
 
622
void
 
623
stop_overlay                    (void)
 
624
{
 
625
  g_assert (tv_info.main_window != NULL);
 
626
 
 
627
  /* XXX see below */
 
628
  z_video_set_max_size (Z_VIDEO (tv_info.video_window), 16384, 16384);
 
629
 
 
630
  g_signal_handlers_disconnect_matched
 
631
    (G_OBJECT (tv_info.main_window),
 
632
     G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
 
633
     0, 0, NULL, G_CALLBACK (on_main_window_delete_event), NULL);
 
634
 
 
635
  g_signal_handlers_disconnect_matched
 
636
    (G_OBJECT (tv_info.video_window),
 
637
     G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
 
638
     0, 0, NULL, G_CALLBACK (on_video_window_event), NULL);
 
639
 
 
640
  if (tv_info.needs_cleaning)
 
641
    {
 
642
      gdk_window_remove_filter (tv_info.root_window, root_filter, NULL);
 
643
 
 
644
      g_signal_handlers_disconnect_matched
 
645
        (G_OBJECT (tv_info.main_window),
 
646
         G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
 
647
         0, 0, NULL, G_CALLBACK (on_main_window_event), NULL);
 
648
 
 
649
      g_signal_handlers_disconnect_matched
 
650
        (G_OBJECT (osd_model),
 
651
         G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
 
652
         0, 0, NULL, G_CALLBACK (on_osd_model_changed), NULL);
 
653
    }
 
654
 
 
655
  terminate ();
 
656
 
 
657
  tv_clip_vector_destroy (&tv_info.set_vector);
 
658
  tv_clip_vector_destroy (&tv_info.tmp_vector);
 
659
  tv_clip_vector_destroy (&tv_info.cur_vector);
 
660
 
 
661
  tv_set_capture_mode (tv_info.info, CAPTURE_MODE_NONE);
 
662
 
 
663
  CLEAR (tv_info);
 
664
}
 
665
 
 
666
gboolean
 
667
start_overlay                   (void)
 
668
{
 
669
  GdkEventMask mask;
 
670
 
 
671
  tv_info.main_window = GTK_WIDGET (zapping);
 
672
  tv_info.video_window = GTK_WIDGET (zapping->video);
 
673
 
 
674
  /* XXX no const limit please */
 
675
  z_video_set_max_size (zapping->video, 768, 576);
 
676
 
 
677
  tv_info.info = zapping->info;
 
678
 
 
679
  gdk_window_get_origin (GTK_WIDGET (zapping)->window,
 
680
                         &tv_info.mw_x, &tv_info.mw_y);
 
681
 
 
682
  gdk_window_get_geometry (GTK_WIDGET (zapping->video)->window,
 
683
                           &tv_info.vw_x, &tv_info.vw_y,
 
684
                           &tv_info.vw_width, &tv_info.vw_height,
 
685
                           /* depth */ NULL);
 
686
 
 
687
  tv_info.screen = tv_screen_list_find (screens,
 
688
                                        tv_info.mw_x + tv_info.vw_x,
 
689
                                        tv_info.mw_y + tv_info.vw_y,
 
690
                                        (guint) tv_info.vw_width,
 
691
                                        (guint) tv_info.vw_height);
 
692
  if (!tv_info.screen)
 
693
    tv_info.screen = screens;
 
694
 
 
695
  CLEAR (tv_info.window);
 
696
 
 
697
  tv_clip_vector_init (&tv_info.cur_vector);
 
698
  tv_clip_vector_init (&tv_info.tmp_vector);
 
699
  tv_clip_vector_init (&tv_info.set_vector);
 
700
 
 
701
  tv_info.visibility            = GDK_VISIBILITY_PARTIAL; /* assume worst */
 
702
 
 
703
  tv_info.clean_screen          = FALSE;
 
704
  tv_info.geometry_changed      = TRUE;
 
705
 
 
706
  tv_info.timeout_id            = NO_SOURCE_ID;
 
707
 
 
708
  /* Make sure we use an Xv adaptor which can render into da->window.
 
709
     (Won't help with X.org but it's the right thing to do.) */
 
710
  tveng_close_device(zapping->info);
 
711
  if (-1 == tveng_attach_device
 
712
      (zcg_char (NULL, "video_device"),
 
713
       GDK_WINDOW_XWINDOW (GTK_WIDGET (zapping->video)->window),
 
714
       TVENG_ATTACH_XV, zapping->info))
 
715
    {
 
716
      ShowBox("Overlay mode not available:\n%s",
 
717
              GTK_MESSAGE_ERROR, tv_get_errstr (zapping->info));
 
718
      goto failure;
 
719
    }
 
720
 
 
721
  tv_info.needs_cleaning =
 
722
    (tv_get_controller (zapping->info) != TVENG_CONTROLLER_XV);
 
723
 
 
724
  if (tv_get_controller (zapping->info) != TVENG_CONTROLLER_XV
 
725
      && (OVERLAY_CHROMA_TEST
 
726
          || (tv_get_caps (zapping->info)->flags & TVENG_CAPS_CHROMAKEY)))
 
727
    {
 
728
      GdkColor chroma;
 
729
 
 
730
      CLEAR (chroma);
 
731
 
 
732
      if (OVERLAY_CHROMA_TEST)
 
733
        chroma.red = 0xffff;
 
734
      else
 
735
        chroma.blue = 0xffff;
 
736
 
 
737
      if (gdk_colormap_alloc_color (gdk_colormap_get_system (),
 
738
                                    &chroma, FALSE, TRUE))
 
739
        {
 
740
          z_set_window_bg (GTK_WIDGET (zapping->video), &chroma);
 
741
 
 
742
          /* XXX safe? (we run on 15/16/24/32 yet, but 8 bit later?) */
 
743
          gdk_colormap_free_colors (gdk_colormap_get_system(), &chroma, 1);
 
744
        }
 
745
      else
 
746
        {
 
747
          ShowBox ("Couldn't allocate chromakey, chroma won't work",
 
748
                   GTK_MESSAGE_WARNING);
 
749
        }
 
750
    }
 
751
  else if (tv_get_controller (zapping->info) == TVENG_CONTROLLER_XV)
 
752
    {
 
753
      GdkColor chroma;
 
754
      unsigned int chromakey;
 
755
      CLEAR (chroma);
 
756
 
 
757
      /* Error ignored */
 
758
      tv_get_overlay_chromakey (zapping->info, &chromakey);
 
759
 
 
760
      chroma.pixel = chromakey;
 
761
 
 
762
      z_set_window_bg (GTK_WIDGET (zapping->video), &chroma);
 
763
    }
 
764
 
 
765
  /* Disable double buffering just in case, will help when a
 
766
     XV driver doesn't provide XV_COLORKEY but requires the colorkey
 
767
     not to be overwritten */
 
768
  gtk_widget_set_double_buffered (GTK_WIDGET (zapping->video), FALSE);
 
769
 
 
770
  if (TVENG_CONTROLLER_XV == tv_get_controller (zapping->info))
 
771
    {
 
772
      if (!tv_set_overlay_xwindow
 
773
          (tv_info.info,
 
774
           GDK_WINDOW_XWINDOW (GTK_WIDGET (zapping->video)->window),
 
775
           GDK_GC_XGC (GTK_WIDGET (zapping->video)->style->white_gc)))
 
776
        goto failure;
 
777
 
 
778
      /* Just update overlay on video_window size change. */
 
779
 
 
780
      g_signal_connect (G_OBJECT (zapping->video), "event",
 
781
                        G_CALLBACK (on_video_window_event), NULL);
 
782
 
 
783
      mask = gdk_window_get_events (GTK_WIDGET (zapping->video)->window);
 
784
      mask |= GDK_CONFIGURE;
 
785
      gdk_window_set_events (GTK_WIDGET (zapping->video)->window, mask);
 
786
    }
 
787
  else
 
788
    {
 
789
      if (!z_set_overlay_buffer (zapping->info,
 
790
                                 tv_info.screen,
 
791
                                 GTK_WIDGET (zapping->video)->window))
 
792
        goto failure;
 
793
 
 
794
      g_signal_connect (G_OBJECT (osd_model), "changed",
 
795
                        G_CALLBACK (on_osd_model_changed), NULL);
 
796
 
 
797
      g_signal_connect (G_OBJECT (zapping->video), "event",
 
798
                        G_CALLBACK (on_video_window_event), NULL);
 
799
 
 
800
      mask = gdk_window_get_events (GTK_WIDGET (zapping->video)->window);
 
801
      mask |= GDK_VISIBILITY_NOTIFY_MASK | GDK_CONFIGURE | GDK_EXPOSE;
 
802
      gdk_window_set_events (GTK_WIDGET (zapping->video)->window, mask);
 
803
 
 
804
      /* We must connect to main_window because the video_window
 
805
         GDK_CONFIGURE event notifies only about main_window relative,
 
806
         not absolute i.e. root window relative position changes.
 
807
         GDK_UNMAP, but no visibility event, is sent after the window
 
808
         was rolled up or minimized. */
 
809
 
 
810
      g_signal_connect (G_OBJECT (zapping), "event",
 
811
                        G_CALLBACK (on_main_window_event), NULL);
 
812
 
 
813
      mask = gdk_window_get_events (GTK_WIDGET (zapping)->window);
 
814
      mask |= GDK_UNMAP | GDK_CONFIGURE;
 
815
      gdk_window_set_events (GTK_WIDGET (zapping)->window, mask);
 
816
 
 
817
      /* There is no GDK_OBSCURED event, we must monitor all child
 
818
         windows of the root window. E.g. drop-down menus. */
 
819
 
 
820
      tv_info.root_window = gdk_get_default_root_window ();
 
821
 
 
822
      gdk_window_add_filter (tv_info.root_window, root_filter, NULL);
 
823
 
 
824
      mask = GDK_STRUCTURE_MASK | GDK_SUBSTRUCTURE_MASK;
 
825
      gdk_window_set_events (tv_info.root_window, mask);
 
826
    }
 
827
 
 
828
  g_signal_connect (G_OBJECT (zapping), "delete-event",
 
829
                    G_CALLBACK (on_main_window_delete_event), NULL);
 
830
 
 
831
  /* Start overlay */
 
832
 
 
833
  if (tv_info.needs_cleaning)
 
834
    {
 
835
      restart_timeout ();
 
836
    }
 
837
  else
 
838
    {
 
839
      /* XXX tveng_set_preview_window (currently default is
 
840
         fill window size) */
 
841
 
 
842
      if (!OVERLAY_CHROMA_TEST)
 
843
        {
 
844
          /* XXX error ignored */
 
845
          tv_enable_overlay (tv_info.info, TRUE);
 
846
        }
 
847
    }
 
848
 
 
849
  zapping->display_mode = DISPLAY_MODE_WINDOW;
 
850
  tv_set_capture_mode (zapping->info, CAPTURE_MODE_OVERLAY);
 
851
 
 
852
  return TRUE;
 
853
 
 
854
 failure:
 
855
  return FALSE;
540
856
}