~ubuntu-branches/debian/sid/cheese/sid

« back to all changes in this revision

Viewing changes to libcheese/cheese-widget.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Biebl
  • Date: 2010-05-04 17:37:18 UTC
  • mfrom: (1.1.13 upstream)
  • Revision ID: james.westby@ubuntu.com-20100504173718-k2rx3nryi4vd0xyx
Tags: 2.30.1-1
* New upstream release.
  - HAL dependency has been dropped. Use (g)udev for v4l capability probing
    on Linux. Closes: #573774
  - Split code into separate libraries.
* debian/control.in
  - Drop Build-Depends on libhal-dev.
  - Drop Build-Depends on libebook1.2-dev.
  - Bump Build-Depends on libgtk2.0-dev to (>= 2.19.1).
  - Bump Build-Depends on libgstreamer*-dev to (>= 0.10.23).
  - Add Build-Depends on libcanberra-gtk-dev.
  - Add Build-Depends on libxtst-dev.
  - Add Build-Depends on libgudev-1.0-dev on Linux.
  - Bump Standards-Version to 3.8.4. No further changes.
* Switch to source format 3.0 (quilt)
  - Add debian/source/format.
* debian/rules
  - Drop lpia specific configure flags, lpia is dead.
* Update package layout (based on work by Ubuntu)
  - Move data files into new cheese-common package.
  - Keep binary along with its desktop and dbus service file in the cheese
    package.
  - Add libcheese-gtk18 and libcheese-gtk-dev package for the new
    libcheese-gtk library. Use a symbols file for improved shlibs
    dependencies.
  - Add Conflicts/Replaces to cheese-common to ensure proper upgrades from
    previous versions.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2009 Bastien Nocera <hadess@hadess.net>
 
3
 *
 
4
 * Licensed under the GNU General Public License Version 2
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include "cheese-config.h"
 
21
 
 
22
#include <glib/gi18n.h>
 
23
 
 
24
#include "cheese-widget.h"
 
25
#include "cheese-gconf.h"
 
26
#include "cheese-camera.h"
 
27
#include "cheese-enum-types.h"
 
28
 
 
29
enum
 
30
{
 
31
  READY_SIGNAL,
 
32
  ERROR_SIGNAL,
 
33
  LAST_SIGNAL
 
34
};
 
35
 
 
36
enum
 
37
{
 
38
  PROP_0,
 
39
  PROP_STATE
 
40
};
 
41
 
 
42
enum
 
43
{
 
44
  SPINNER_PAGE = 0,
 
45
  WEBCAM_PAGE  = 1,
 
46
  PROBLEM_PAGE = 2,
 
47
};
 
48
 
 
49
typedef struct
 
50
{
 
51
  GtkWidget *spinner;
 
52
  GtkWidget *screen;
 
53
  GtkWidget *problem;
 
54
  CheeseGConf *gconf;
 
55
  CheeseCamera *webcam;
 
56
  CheeseWidgetState state;
 
57
  GError *error;
 
58
} CheeseWidgetPrivate;
 
59
 
 
60
#define CHEESE_WIDGET_GET_PRIVATE(o)                     \
 
61
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHEESE_TYPE_WIDGET, \
 
62
                                CheeseWidgetPrivate))
 
63
 
 
64
G_DEFINE_TYPE (CheeseWidget, cheese_widget, GTK_TYPE_NOTEBOOK);
 
65
 
 
66
static GdkPixbuf *
 
67
cheese_widget_load_pixbuf (GtkWidget  *widget,
 
68
                           const char *icon_name,
 
69
                           guint       size,
 
70
                           GError    **error)
 
71
{
 
72
  GtkIconTheme *theme;
 
73
  GdkPixbuf    *pixbuf;
 
74
 
 
75
  theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
 
76
 
 
77
  /* FIXME special case "no-webcam" and actually use the icon_name */
 
78
  pixbuf = gtk_icon_theme_load_icon (theme, "dialog-error",
 
79
                                     size, GTK_ICON_LOOKUP_USE_BUILTIN | GTK_ICON_LOOKUP_FORCE_SIZE, error);
 
80
  return pixbuf;
 
81
}
 
82
 
 
83
static gboolean
 
84
cheese_widget_logo_expose (GtkWidget      *w,
 
85
                           GdkEventExpose *event,
 
86
                           gpointer        user_data)
 
87
{
 
88
  const char   *icon_name;
 
89
  GdkPixbuf    *pixbuf, *logo;
 
90
  GError       *error = NULL;
 
91
  cairo_t      *cr;
 
92
  GtkAllocation allocation;
 
93
  guint         s_width, s_height, d_width, d_height;
 
94
  float         ratio;
 
95
 
 
96
  gtk_widget_get_allocation (w, &allocation);
 
97
 
 
98
  gdk_draw_rectangle (gtk_widget_get_window (w),
 
99
                      gtk_widget_get_style (w)->black_gc, TRUE,
 
100
                      0, 0, allocation.width, allocation.height);
 
101
  icon_name = g_object_get_data (G_OBJECT (w), "icon-name");
 
102
  if (icon_name == NULL)
 
103
    return FALSE;
 
104
 
 
105
  cr = gdk_cairo_create (gtk_widget_get_window (w));
 
106
  cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
 
107
  cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
 
108
 
 
109
  d_width  = allocation.width;
 
110
  d_height = allocation.height - (allocation.height / 3);
 
111
 
 
112
  pixbuf = cheese_widget_load_pixbuf (w, icon_name, d_height, &error);
 
113
  if (pixbuf == NULL)
 
114
  {
 
115
    g_warning ("Could not load icon '%s': %s", icon_name, error->message);
 
116
    g_error_free (error);
 
117
    return FALSE;
 
118
  }
 
119
 
 
120
  s_width  = gdk_pixbuf_get_width (pixbuf);
 
121
  s_height = gdk_pixbuf_get_height (pixbuf);
 
122
 
 
123
  if ((gfloat) d_width / s_width > (gfloat) d_height / s_height)
 
124
  {
 
125
    ratio = (gfloat) d_height / s_height;
 
126
  }
 
127
  else
 
128
  {
 
129
    ratio = (gfloat) d_width / s_width;
 
130
  }
 
131
 
 
132
  s_width  *= ratio;
 
133
  s_height *= ratio;
 
134
 
 
135
  logo = gdk_pixbuf_scale_simple (pixbuf, s_width, s_height, GDK_INTERP_BILINEAR);
 
136
 
 
137
  gdk_cairo_set_source_pixbuf (cr, logo, (allocation.width - s_width) / 2, (allocation.height - s_height) / 2);
 
138
  cairo_paint (cr);
 
139
  cairo_destroy (cr);
 
140
 
 
141
  g_object_unref (logo);
 
142
  g_object_unref (pixbuf);
 
143
 
 
144
  return FALSE;
 
145
}
 
146
 
 
147
static void
 
148
cheese_widget_spinner_invert (GtkWidget *spinner, GtkWidget *parent)
 
149
{
 
150
  GtkStyle *style;
 
151
  guint     i;
 
152
 
 
153
  for (i = GTK_STATE_NORMAL; i <= GTK_STATE_INSENSITIVE; i++)
 
154
  {
 
155
    GdkColor *fg, *bg;
 
156
 
 
157
    style = gtk_widget_get_style (spinner);
 
158
    fg    = gdk_color_copy (&style->fg[i]);
 
159
    bg    = gdk_color_copy (&style->bg[i]);
 
160
 
 
161
    gtk_widget_modify_fg (spinner, i, bg);
 
162
    gtk_widget_modify_bg (spinner, i, fg);
 
163
 
 
164
    gtk_widget_modify_fg (parent, i, bg);
 
165
    gtk_widget_modify_bg (parent, i, fg);
 
166
 
 
167
    gdk_color_free (fg);
 
168
    gdk_color_free (bg);
 
169
  }
 
170
}
 
171
 
 
172
static void
 
173
cheese_widget_set_problem_page (CheeseWidget *widget,
 
174
                                const char   *icon_name)
 
175
{
 
176
  CheeseWidgetPrivate *priv = CHEESE_WIDGET_GET_PRIVATE (widget);
 
177
 
 
178
  gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), PROBLEM_PAGE);
 
179
  g_object_set_data_full (G_OBJECT (priv->problem),
 
180
                          "icon-name", g_strdup (icon_name), g_free);
 
181
  g_signal_connect (priv->problem, "expose-event",
 
182
                    G_CALLBACK (cheese_widget_logo_expose), widget);
 
183
}
 
184
 
 
185
static void
 
186
cheese_widget_init (CheeseWidget *widget)
 
187
{
 
188
  CheeseWidgetPrivate *priv = CHEESE_WIDGET_GET_PRIVATE (widget);
 
189
  GtkWidget           *box;
 
190
 
 
191
  priv->state = CHEESE_WIDGET_STATE_NONE;
 
192
  priv->error = NULL;
 
193
 
 
194
  /* XXX
 
195
   * remove this line if you want to debug */
 
196
  gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
 
197
  gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE);
 
198
 
 
199
  /* Spinner page */
 
200
  priv->spinner = gtk_spinner_new ();
 
201
  box           = gtk_event_box_new ();
 
202
  gtk_container_add (GTK_CONTAINER (box), priv->spinner);
 
203
  cheese_widget_spinner_invert (priv->spinner, box);
 
204
  gtk_widget_show_all (box);
 
205
 
 
206
  gtk_notebook_append_page (GTK_NOTEBOOK (widget),
 
207
                            box, gtk_label_new ("spinner"));
 
208
 
 
209
  /* Webcam page */
 
210
  priv->screen = gtk_drawing_area_new ();
 
211
  gtk_widget_show (priv->screen);
 
212
  gtk_notebook_append_page (GTK_NOTEBOOK (widget),
 
213
                            priv->screen, gtk_label_new ("webcam"));
 
214
 
 
215
  /* Problem page */
 
216
  priv->problem = gtk_drawing_area_new ();
 
217
  gtk_widget_show (priv->problem);
 
218
  gtk_notebook_append_page (GTK_NOTEBOOK (widget),
 
219
                            priv->problem,
 
220
                            gtk_label_new ("got problems"));
 
221
 
 
222
  priv->gconf = cheese_gconf_new ();
 
223
}
 
224
 
 
225
static void
 
226
cheese_widget_finalize (GObject *object)
 
227
{
 
228
  CheeseWidgetPrivate *priv = CHEESE_WIDGET_GET_PRIVATE (object);
 
229
 
 
230
  if (priv->gconf)
 
231
  {
 
232
    g_object_unref (priv->gconf);
 
233
    priv->gconf = NULL;
 
234
  }
 
235
  if (priv->webcam)
 
236
  {
 
237
    g_object_unref (priv->webcam);
 
238
    priv->webcam = NULL;
 
239
  }
 
240
 
 
241
  G_OBJECT_CLASS (cheese_widget_parent_class)->finalize (object);
 
242
}
 
243
 
 
244
#if 0
 
245
static void
 
246
cheese_widget_set_property (GObject *object, guint prop_id,
 
247
                            const GValue *value,
 
248
                            GParamSpec *pspec)
 
249
{
 
250
  CheeseWidgetPrivate *priv = CHEESE_WIDGET_GET_PRIVATE (object);
 
251
 
 
252
  g_return_if_fail (CHEESE_IS_WIDGET (object));
 
253
 
 
254
  switch (prop_id)
 
255
  {
 
256
    case PROP_STATE:
 
257
      priv->state = g_value_get_enum (value);
 
258
      break;
 
259
    default:
 
260
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
261
      break;
 
262
  }
 
263
}
 
264
#endif
 
265
 
 
266
static void
 
267
cheese_widget_get_property (GObject *object, guint prop_id,
 
268
                            GValue *value, GParamSpec *pspec)
 
269
{
 
270
  CheeseWidgetPrivate *priv = CHEESE_WIDGET_GET_PRIVATE (object);
 
271
 
 
272
  g_return_if_fail (CHEESE_IS_WIDGET (object));
 
273
 
 
274
  switch (prop_id)
 
275
  {
 
276
    case PROP_STATE:
 
277
      g_value_set_enum (value, priv->state);
 
278
      break;
 
279
    default:
 
280
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
281
      break;
 
282
  }
 
283
}
 
284
 
 
285
#if 0
 
286
static void
 
287
cheese_widget_changed (CheeseWidget *self)
 
288
{
 
289
}
 
290
 
 
291
#endif
 
292
 
 
293
void
 
294
setup_camera (CheeseWidget *widget)
 
295
{
 
296
  CheeseWidgetPrivate *priv          = CHEESE_WIDGET_GET_PRIVATE (widget);
 
297
  char                *webcam_device = NULL;
 
298
  int                  x_resolution;
 
299
  int                  y_resolution;
 
300
  gdouble              brightness;
 
301
  gdouble              contrast;
 
302
  gdouble              saturation;
 
303
  gdouble              hue;
 
304
 
 
305
  g_object_get (priv->gconf,
 
306
                "gconf_prop_x_resolution", &x_resolution,
 
307
                "gconf_prop_y_resolution", &y_resolution,
 
308
                "gconf_prop_camera", &webcam_device,
 
309
                "gconf_prop_brightness", &brightness,
 
310
                "gconf_prop_contrast", &contrast,
 
311
                "gconf_prop_saturation", &saturation,
 
312
                "gconf_prop_hue", &hue,
 
313
                NULL);
 
314
 
 
315
  gdk_threads_enter ();
 
316
  priv->webcam = cheese_camera_new (priv->screen,
 
317
                                    webcam_device,
 
318
                                    x_resolution,
 
319
                                    y_resolution);
 
320
  gdk_threads_leave ();
 
321
 
 
322
  g_free (webcam_device);
 
323
 
 
324
  cheese_camera_setup (priv->webcam, NULL, &priv->error);
 
325
 
 
326
  gdk_threads_enter ();
 
327
 
 
328
  gtk_spinner_stop (GTK_SPINNER (priv->spinner));
 
329
 
 
330
  if (priv->error != NULL)
 
331
  {
 
332
    priv->state = CHEESE_WIDGET_STATE_ERROR;
 
333
    g_object_notify (G_OBJECT (widget), "state");
 
334
    cheese_widget_set_problem_page (CHEESE_WIDGET (widget), "error");
 
335
  }
 
336
  else
 
337
  {
 
338
    cheese_camera_set_balance_property (priv->webcam, "brightness", brightness);
 
339
    cheese_camera_set_balance_property (priv->webcam, "contrast", contrast);
 
340
    cheese_camera_set_balance_property (priv->webcam, "saturation", saturation);
 
341
    cheese_camera_set_balance_property (priv->webcam, "hue", hue);
 
342
    priv->state = CHEESE_WIDGET_STATE_READY;
 
343
    g_object_notify (G_OBJECT (widget), "state");
 
344
    cheese_camera_play (priv->webcam);
 
345
    gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), WEBCAM_PAGE);
 
346
  }
 
347
 
 
348
  gdk_threads_leave ();
 
349
}
 
350
 
 
351
static void
 
352
cheese_widget_realize (GtkWidget *widget)
 
353
{
 
354
  GdkWindow           *window;
 
355
  CheeseWidgetPrivate *priv  = CHEESE_WIDGET_GET_PRIVATE (widget);
 
356
 
 
357
  GTK_WIDGET_CLASS (cheese_widget_parent_class)->realize (widget);
 
358
 
 
359
  gtk_spinner_start (GTK_SPINNER (priv->spinner));
 
360
 
 
361
  gtk_widget_realize (priv->screen);
 
362
  window = gtk_widget_get_window (priv->screen);
 
363
  if (!gdk_window_ensure_native (window))
 
364
  {
 
365
    /* abort: no native window, no xoverlay, no cheese. */
 
366
    g_warning ("Could not create a native X11 window for the drawing area");
 
367
    goto error;
 
368
  }
 
369
 
 
370
  gdk_window_set_back_pixmap (gtk_widget_get_window (priv->screen), NULL, FALSE);
 
371
  gtk_widget_set_app_paintable (priv->screen, TRUE);
 
372
  gtk_widget_set_double_buffered (priv->screen, FALSE);
 
373
 
 
374
  if (!g_thread_create ((GThreadFunc) setup_camera, widget, FALSE, &priv->error))
 
375
  {
 
376
    g_warning ("Failed to create setup thread: %s", priv->error->message);
 
377
    goto error;
 
378
  }
 
379
 
 
380
  gtk_widget_set_app_paintable (priv->problem, TRUE);
 
381
  gtk_widget_realize (priv->problem);
 
382
  gdk_window_set_back_pixmap (gtk_widget_get_window (priv->problem), NULL, FALSE);
 
383
 
 
384
  return;
 
385
 
 
386
error:
 
387
  gtk_spinner_stop (GTK_SPINNER (priv->spinner));
 
388
  priv->state = CHEESE_WIDGET_STATE_ERROR;
 
389
  g_object_notify (G_OBJECT (widget), "state");
 
390
  cheese_widget_set_problem_page (CHEESE_WIDGET (widget), "error");
 
391
}
 
392
 
 
393
static void
 
394
cheese_widget_class_init (CheeseWidgetClass *klass)
 
395
{
 
396
  GObjectClass   *object_class = G_OBJECT_CLASS (klass);
 
397
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
398
 
 
399
  object_class->finalize = cheese_widget_finalize;
 
400
#if 0
 
401
  object_class->set_property = cheese_widget_set_property;
 
402
#endif
 
403
  object_class->get_property = cheese_widget_get_property;
 
404
  widget_class->realize = cheese_widget_realize;
 
405
 
 
406
  /**
 
407
   * CheeseWidget:state:
 
408
   *
 
409
   * Current state of the widget.
 
410
   *
 
411
   * Connect to notify::state signal to get notified about state
 
412
   * changes. Useful to update other widgets sensitiveness when the
 
413
   * camera is ready or to handle errors if camera setup fails.
 
414
   */
 
415
  g_object_class_install_property (object_class, PROP_STATE,
 
416
                                   g_param_spec_enum ("state",
 
417
                                                      NULL,
 
418
                                                      NULL,
 
419
                                                      CHEESE_TYPE_WIDGET_STATE,
 
420
                                                      CHEESE_WIDGET_STATE_NONE,
 
421
                                                      G_PARAM_READABLE));
 
422
 
 
423
  g_type_class_add_private (klass, sizeof (CheeseWidgetPrivate));
 
424
}
 
425
 
 
426
/**
 
427
 * cheese_widget_new:
 
428
 *
 
429
 * Returns a new #CheeseWidget widget.
 
430
 *
 
431
 * Return value: a #CheeseWidget widget.
 
432
 **/
 
433
GtkWidget *
 
434
cheese_widget_new (void)
 
435
{
 
436
  return g_object_new (CHEESE_TYPE_WIDGET, NULL);
 
437
}
 
438
 
 
439
GObject *
 
440
cheese_widget_get_gconf (CheeseWidget *widget)
 
441
{
 
442
  CheeseWidgetPrivate *priv;
 
443
 
 
444
  g_return_val_if_fail (CHEESE_WIDGET (widget), NULL);
 
445
 
 
446
  priv = CHEESE_WIDGET_GET_PRIVATE (widget);
 
447
 
 
448
  return G_OBJECT (priv->gconf);
 
449
}
 
450
 
 
451
GObject *
 
452
cheese_widget_get_camera (CheeseWidget *widget)
 
453
{
 
454
  CheeseWidgetPrivate *priv;
 
455
 
 
456
  g_return_val_if_fail (CHEESE_WIDGET (widget), NULL);
 
457
 
 
458
  priv = CHEESE_WIDGET_GET_PRIVATE (widget);
 
459
 
 
460
  return G_OBJECT (priv->webcam);
 
461
}
 
462
 
 
463
GtkWidget *
 
464
cheese_widget_get_video_area (CheeseWidget *widget)
 
465
{
 
466
 CheeseWidgetPrivate *priv;
 
467
 
 
468
  g_return_val_if_fail (CHEESE_WIDGET (widget), NULL);
 
469
 
 
470
  priv = CHEESE_WIDGET_GET_PRIVATE (widget);
 
471
 
 
472
  return priv->screen;
 
473
}
 
474
 
 
475
/**
 
476
 * cheese_widget_get_error:
 
477
 * @widget: a #CheeseWidget
 
478
 * @error: return location for the error
 
479
 *
 
480
 * Listen for notify::state signals and call this when the current state is %CHEESE_WIDGET_STATE_ERROR.
 
481
 *
 
482
 * The returned #GError will contain more details on what went wrong.
 
483
 **/
 
484
 
 
485
void
 
486
cheese_widget_get_error (CheeseWidget *widget, GError **error)
 
487
{
 
488
  CheeseWidgetPrivate *priv;
 
489
 
 
490
  g_return_if_fail (CHEESE_WIDGET (widget));
 
491
 
 
492
  priv = CHEESE_WIDGET_GET_PRIVATE (widget);
 
493
 
 
494
  g_propagate_error (error, priv->error);
 
495
 
 
496
  priv->error = NULL;
 
497
}
 
498
 
 
499
/*
 
500
 * vim: sw=2 ts=8 cindent noai bs=2
 
501
 */