Merge lp:~mterry/unity-greeter/grid-fixes-plus-refactor into lp:unity-greeter

Proposed by Michael Terry
Status: Merged
Approved by: Robert Ancell
Approved revision: 250
Merged at revision: 250
Proposed branch: lp:~mterry/unity-greeter/grid-fixes-plus-refactor
Merge into: lp:unity-greeter
Diff against target: 1871 lines (+896/-748)
6 files modified
src/Makefile.am (+2/-0)
src/background.vala (+357/-0)
src/dash-box.vala (+17/-0)
src/menubar.vala (+466/-0)
src/unity-greeter.vala (+20/-14)
src/user-list.vala (+34/-734)
To merge this branch: bzr merge lp:~mterry/unity-greeter/grid-fixes-plus-refactor
Reviewer Review Type Date Requested Status
Robert Ancell Approve
Review via email: mp+88602@code.launchpad.net

Description of the change

(This branch is lacking in history because I accidentally blew it away when I deleted my checkout. Thankfully I had the source files open in gedit and didn't lose the work, but I did lose the bzr history.)

This branch's primary purpose was to enable hiding the grid dots behind the login box and the menubar. But along the way, I refactored the background and menubar code out of UserList and fixed the logo to align with the grid better.

To post a comment you must log in.
Revision history for this message
Robert Ancell (robert-ancell) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/logo.png'
2Binary files data/logo.png 2012-01-12 16:38:55 +0000 and data/logo.png 2012-01-15 10:29:23 +0000 differ
3=== modified file 'src/Makefile.am'
4--- src/Makefile.am 2012-01-12 13:17:02 +0000
5+++ src/Makefile.am 2012-01-15 10:29:23 +0000
6@@ -7,7 +7,9 @@
7 fixes.vapi \
8 indicator.vapi \
9 animate-timer.vala \
10+ background.vala \
11 dash-box.vala \
12+ menubar.vala \
13 settings-daemon.vala \
14 unity-greeter.vala \
15 user-list.vala
16
17=== added file 'src/background.vala'
18--- src/background.vala 1970-01-01 00:00:00 +0000
19+++ src/background.vala 2012-01-15 10:29:23 +0000
20@@ -0,0 +1,357 @@
21+/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 4 -*-
22+ *
23+ * Copyright (C) 2011,2012 Canonical Ltd
24+ *
25+ * This program is free software: you can redistribute it and/or modify
26+ * it under the terms of the GNU General Public License version 3 as
27+ * published by the Free Software Foundation.
28+ *
29+ * This program is distributed in the hope that it will be useful,
30+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
31+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32+ * GNU General Public License for more details.
33+ *
34+ * You should have received a copy of the GNU General Public License
35+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
36+ *
37+ * Authors: Robert Ancell <robert.ancell@canonical.com>
38+ * Michael Terry <michael.terry@canonical.com>
39+ */
40+
41+class BackgroundLoader
42+{
43+ public string filename {get; private set;}
44+ public Cairo.Surface logo {get; set;}
45+
46+ private int width;
47+ private int height;
48+ private bool draw_grid;
49+ private unowned Thread<Cairo.Pattern?> thread;
50+ private bool finished;
51+ private uint ready_id;
52+
53+ public Cairo.Pattern? pattern = null;
54+
55+ public signal void loaded ();
56+
57+ public BackgroundLoader (string filename, int width, int height, bool draw_grid)
58+ {
59+ this.filename = filename;
60+ this.width = width;
61+ this.height = height;
62+ this.draw_grid = draw_grid;
63+ }
64+
65+ public bool load ()
66+ {
67+ /* Already loaded */
68+ if (finished)
69+ return true;
70+
71+ /* Currently loading */
72+ if (thread != null)
73+ return false;
74+
75+ debug ("Making background %s at %dx%d", filename, width, height);
76+ try
77+ {
78+ thread = Thread<Cairo.Pattern?>.create<Cairo.Pattern?> (render, true);
79+ }
80+ catch (ThreadError e)
81+ {
82+ finished = true;
83+ return true;
84+ }
85+
86+ return false;
87+ }
88+
89+ ~Background ()
90+ {
91+ if (ready_id > 0)
92+ Source.remove (ready_id);
93+ ready_id = 0;
94+ }
95+
96+ private bool ready_cb ()
97+ {
98+ debug ("Render of background %s at %dx%d complete", filename, width, height);
99+
100+ pattern = thread.join ();
101+ thread = null;
102+ finished = true;
103+
104+ loaded ();
105+
106+ return false;
107+ }
108+
109+ private Cairo.Pattern? render ()
110+ {
111+ Gdk.Color color;
112+ Gdk.Pixbuf orig_image = null;
113+ if (!Gdk.Color.parse (filename, out color))
114+ {
115+ try
116+ {
117+ orig_image = new Gdk.Pixbuf.from_file (filename);
118+ }
119+ catch (Error e)
120+ {
121+ debug ("Error loading background: %s", e.message);
122+ ready_id = Gdk.threads_add_idle (ready_cb);
123+ return null;
124+ }
125+ }
126+
127+ Gdk.Pixbuf? image = null;
128+ if (orig_image != null)
129+ {
130+ var target_aspect = (double) width / height;
131+ var aspect = (double) orig_image.width / orig_image.height;
132+ double scale, offset_x = 0, offset_y = 0;
133+ if (aspect > target_aspect)
134+ {
135+ /* Fit height and trim sides */
136+ scale = (double) height / orig_image.height;
137+ offset_x = (orig_image.width * scale - width) / 2;
138+ }
139+ else
140+ {
141+ /* Fit width and trim top and bottom */
142+ scale = (double) width / orig_image.width;
143+ offset_y = (orig_image.height * scale - height) / 2;
144+ }
145+
146+ image = new Gdk.Pixbuf (orig_image.colorspace, orig_image.has_alpha, orig_image.bits_per_sample, width, height);
147+ orig_image.scale (image, 0, 0, width, height, -offset_x, -offset_y, scale, scale, Gdk.InterpType.BILINEAR);
148+ }
149+
150+ var grid_x_offset = get_grid_offset (width);
151+ var grid_y_offset = get_grid_offset (height);
152+
153+ /* Create background */
154+ var surface = new Cairo.ImageSurface (Cairo.Format.RGB24, width, height);
155+ var bc = new Cairo.Context (surface);
156+ if (image != null)
157+ Gdk.cairo_set_source_pixbuf (bc, image, 0, 0);
158+ else
159+ Gdk.cairo_set_source_color (bc, color);
160+ bc.paint ();
161+
162+ /* Draw logo */
163+ if (logo != null)
164+ {
165+ bc.save ();
166+ var y = (int) (height / grid_size - 2) * grid_size + grid_y_offset;
167+ bc.translate (grid_x_offset, y);
168+ bc.set_source_surface (logo, 0, 0);
169+ bc.paint_with_alpha (0.5);
170+ bc.restore ();
171+ }
172+
173+ var pattern = new Cairo.Pattern.for_surface (surface);
174+ pattern.set_extend (Cairo.Extend.REPEAT);
175+
176+ ready_id = Gdk.threads_add_idle (ready_cb);
177+
178+ return pattern;
179+ }
180+}
181+
182+public class Background : Gtk.Box
183+{
184+ public enum DrawFlags
185+ {
186+ NONE,
187+ GRID,
188+ }
189+
190+ public string default_background {get; set; default = "#2C001E";}
191+ public string? current_background {get; set; default = null;}
192+ public bool draw_grid {get; set; default = true;}
193+ public double alpha {get; private set; default = 1.0;}
194+
195+ public void set_logo (string logo_path)
196+ {
197+ if (FileUtils.test (logo_path, FileTest.EXISTS))
198+ {
199+ logo_surface = new Cairo.ImageSurface.from_png (logo_path);
200+ }
201+ else
202+ {
203+ debug ("Can't use logo %s, it does not exist", logo_path);
204+ logo_surface = null;
205+ }
206+ }
207+
208+ public override void size_allocate (Gtk.Allocation allocation)
209+ {
210+ var resized = allocation.height != get_allocated_height () || allocation.width != get_allocated_width ();
211+
212+ base.size_allocate (allocation);
213+
214+ /* Regenerate backgrounds */
215+ if (resized)
216+ {
217+ debug ("Regenerating backgrounds");
218+ loaders.remove_all ();
219+ load_background (null);
220+ reload ();
221+ }
222+ }
223+
224+ public override bool draw (Cairo.Context c)
225+ {
226+ var flags = DrawFlags.NONE;
227+ if (draw_grid)
228+ flags |= DrawFlags.GRID;
229+ draw_full (c, flags);
230+ return base.draw (c);
231+ }
232+
233+ public void draw_full (Cairo.Context c, DrawFlags flags)
234+ {
235+ c.save ();
236+
237+ /* Test whether we ran into an error loading this background */
238+ if (current == null || (current.load () && current.pattern == null))
239+ {
240+ /* We couldn't load it, so swap it out for the default background
241+ and remember that choice */
242+ var new_background = load_background (null);
243+ if (current != null)
244+ loaders.insert (current.filename, new_background);
245+ if (old == current)
246+ old = new_background;
247+ current = new_background;
248+ }
249+
250+ /* Fade to this background when loaded */
251+ if (current.load () && current != old && !timer.is_running)
252+ {
253+ alpha = 0.0;
254+ timer.reset ();
255+ }
256+
257+ c.set_source_rgba (0.0, 0.0, 0.0, 0.0);
258+ var old_painted = false;
259+
260+ /* Draw old background */
261+ if (old != null && old.load () && (alpha < 1.0 || !current.load ()))
262+ {
263+ c.set_source (old.pattern);
264+ c.paint ();
265+ old_painted = true;
266+ }
267+
268+ /* Draw new background */
269+ if (current.load () && alpha > 0.0)
270+ {
271+ c.set_source (current.pattern);
272+ c.paint_with_alpha (old_painted ? alpha : 1.0);
273+ }
274+
275+ c.restore ();
276+
277+ if ((flags & DrawFlags.GRID) != 0)
278+ overlay_grid (c);
279+ }
280+
281+ private AnimateTimer timer;
282+
283+ private BackgroundLoader current;
284+ private BackgroundLoader old;
285+
286+ private HashTable<string, BackgroundLoader> loaders;
287+
288+ private Cairo.Surface? logo_surface = null;
289+
290+ construct
291+ {
292+ orientation = Gtk.Orientation.VERTICAL;
293+
294+ timer = new AnimateTimer (AnimateTimer.Speed.INSTANT);
295+ timer.animate.connect (animate_cb);
296+
297+ loaders = new HashTable<string?, BackgroundLoader> (str_hash, str_equal);
298+
299+ notify["current-background"].connect (() => {reload ();});
300+ }
301+
302+ private void animate_cb (double progress)
303+ {
304+ alpha = progress;
305+
306+ queue_draw ();
307+
308+ /* Stop when we get there */
309+ if (alpha >= 1.0)
310+ old = current;
311+ }
312+
313+ private void reload ()
314+ {
315+ if (get_realized ())
316+ {
317+ var new_background = load_background (current_background);
318+
319+ if (current != new_background)
320+ {
321+ old = current;
322+ current = new_background;
323+ }
324+
325+ queue_draw ();
326+ }
327+ }
328+
329+ private BackgroundLoader load_background (string? filename)
330+ {
331+ if (filename == null)
332+ filename = default_background;
333+
334+ var b = loaders.lookup (filename);
335+ if (b == null)
336+ {
337+ b = new BackgroundLoader (filename, get_allocated_width (),
338+ get_allocated_height (), draw_grid);
339+ b.logo = logo_surface;
340+ b.loaded.connect (() => {reload();});
341+ b.load ();
342+ loaders.insert (filename, b);
343+ }
344+
345+ return b;
346+ }
347+
348+ private void overlay_grid (Cairo.Context c)
349+ {
350+ var width = get_allocated_width ();
351+ var height = get_allocated_height ();
352+ var grid_x_offset = get_grid_offset (width);
353+ var grid_y_offset = get_grid_offset (height);
354+
355+ /* Overlay grid */
356+ var overlay_surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, grid_size, grid_size);
357+ var oc = new Cairo.Context (overlay_surface);
358+ oc.rectangle (0, 0, 1, 1);
359+ oc.rectangle (grid_size - 1, 0, 1, 1);
360+ oc.rectangle (0, grid_size - 1, 1, 1);
361+ oc.rectangle (grid_size - 1, grid_size - 1, 1, 1);
362+ oc.set_source_rgba (1.0, 1.0, 1.0, 0.25);
363+ oc.fill ();
364+ var overlay = new Cairo.Pattern.for_surface (overlay_surface);
365+ var matrix = Cairo.Matrix.identity ();
366+ matrix.translate (-grid_x_offset, -grid_y_offset);
367+ overlay.set_matrix (matrix);
368+ overlay.set_extend (Cairo.Extend.REPEAT);
369+
370+ /* Draw overlay */
371+ c.save ();
372+ c.set_source (overlay);
373+ c.rectangle (0, 0, width, height);
374+ c.fill ();
375+ c.restore ();
376+ }
377+}
378
379=== modified file 'src/dash-box.vala'
380--- src/dash-box.vala 2012-01-12 12:03:25 +0000
381+++ src/dash-box.vala 2012-01-15 10:29:23 +0000
382@@ -19,6 +19,13 @@
383
384 public class DashBox : Gtk.Box
385 {
386+ public Background? background {get; construct; default = null;}
387+
388+ public DashBox (Background bg)
389+ {
390+ Object (background: bg);
391+ }
392+
393 construct
394 {
395 orientation = Gtk.Orientation.VERTICAL;
396@@ -26,6 +33,16 @@
397
398 public override bool draw (Cairo.Context c)
399 {
400+ if (background != null)
401+ {
402+ int x, y;
403+ background.translate_coordinates (this, 0, 0, out x, out y);
404+ c.save ();
405+ c.translate (x, y);
406+ background.draw_full (c, Background.DrawFlags.NONE);
407+ c.restore ();
408+ }
409+
410 c.save ();
411
412 Gtk.Allocation allocation;
413
414=== added file 'src/menubar.vala'
415--- src/menubar.vala 1970-01-01 00:00:00 +0000
416+++ src/menubar.vala 2012-01-15 10:29:23 +0000
417@@ -0,0 +1,466 @@
418+/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 4 -*-
419+ *
420+ * Copyright (C) 2011,2012 Canonical Ltd
421+ *
422+ * This program is free software: you can redistribute it and/or modify
423+ * it under the terms of the GNU General Public License version 3 as
424+ * published by the Free Software Foundation.
425+ *
426+ * This program is distributed in the hope that it will be useful,
427+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
428+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
429+ * GNU General Public License for more details.
430+ *
431+ * You should have received a copy of the GNU General Public License
432+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
433+ *
434+ * Authors: Robert Ancell <robert.ancell@canonical.com>
435+ * Michael Terry <michael.terry@canonical.com>
436+ */
437+
438+private class IndicatorMenuItem : Gtk.MenuItem
439+{
440+ public unowned Indicator.ObjectEntry entry;
441+ private Gtk.HBox hbox;
442+
443+ public IndicatorMenuItem (Indicator.ObjectEntry entry)
444+ {
445+ this.entry = entry;
446+ this.hbox = new Gtk.HBox (false, 3);
447+ this.add (this.hbox);
448+ this.hbox.show ();
449+
450+ if (entry.label != null)
451+ {
452+ entry.label.show.connect (this.visibility_changed_cb);
453+ entry.label.hide.connect (this.visibility_changed_cb);
454+ hbox.pack_start (entry.label, false, false, 0);
455+ }
456+ if (entry.image != null)
457+ {
458+ entry.image.show.connect (visibility_changed_cb);
459+ entry.image.hide.connect (visibility_changed_cb);
460+ hbox.pack_start (entry.image, false, false, 0);
461+ }
462+ if (entry.accessible_desc != null)
463+ get_accessible ().set_name (entry.accessible_desc);
464+ if (entry.menu != null)
465+ submenu = entry.menu;
466+
467+ if (has_visible_child ())
468+ show ();
469+ }
470+
471+ public bool has_visible_child ()
472+ {
473+ return (entry.image != null && entry.image.get_visible ()) ||
474+ (entry.label != null && entry.label.get_visible ());
475+ }
476+
477+ public void visibility_changed_cb (Gtk.Widget widget)
478+ {
479+ visible = has_visible_child ();
480+ }
481+}
482+
483+public class MenuBar : Gtk.MenuBar
484+{
485+ public Background? background {get; construct; default = null;}
486+ public bool high_contrast {get; private set; default = false;}
487+
488+ public MenuBar (Background bg)
489+ {
490+ Object (background: bg);
491+ }
492+
493+ public void set_layout (LightDM.Layout? layout)
494+ {
495+ if (layout == null)
496+ layout = LightDM.get_layout (); /* default layout */
497+
498+ var item = layout.get_data<Gtk.RadioMenuItem> ("unity-greeter-radio");
499+ if (item != null)
500+ item.active = true; /* will trigger callback to do rest of work */
501+ }
502+
503+ public override bool draw (Cairo.Context c)
504+ {
505+ if (background != null)
506+ {
507+ int x, y;
508+ background.translate_coordinates (this, 0, 0, out x, out y);
509+ c.save ();
510+ c.translate (x, y);
511+ background.draw_full (c, Background.DrawFlags.NONE);
512+ c.restore ();
513+ }
514+
515+ c.set_source_rgb (0, 0, 0);
516+ c.paint_with_alpha (0.5);
517+
518+ foreach (var child in get_children ())
519+ {
520+ propagate_draw (child, c);
521+ }
522+
523+ return false;
524+ }
525+
526+ private string default_theme_name;
527+ private List<Indicator.Object> indicator_objects;
528+ private Gtk.CheckMenuItem high_contrast_item;
529+ private Gtk.Label keyboard_label = null;
530+ private Pid keyboard_pid = 0;
531+ private Gtk.Window? keyboard_window = null;
532+
533+ construct
534+ {
535+ Gtk.Settings.get_default ().get ("gtk-theme-name", out default_theme_name);
536+
537+ pack_direction = Gtk.PackDirection.RTL;
538+
539+ var label = new Gtk.Label (Posix.utsname ().nodename);
540+ label.show ();
541+ var hostname_item = new Gtk.MenuItem ();
542+ hostname_item.add (label);
543+ hostname_item.sensitive = false;
544+ hostname_item.right_justified = true;
545+ hostname_item.show ();
546+ append (hostname_item);
547+
548+ /* Hack to get a label showing on the menubar */
549+ label.ensure_style ();
550+ label.modify_fg (Gtk.StateType.INSENSITIVE, label.get_style ().fg[Gtk.StateType.NORMAL]);
551+
552+ setup_indicators ();
553+ }
554+
555+ ~MenuBar ()
556+ {
557+ if (keyboard_pid != 0)
558+ {
559+ Posix.kill (keyboard_pid, Posix.SIGKILL);
560+ int status;
561+ Posix.waitpid (keyboard_pid, out status, 0);
562+ keyboard_pid = 0;
563+ }
564+ }
565+
566+ private void greeter_set_env (string key, string val)
567+ {
568+ GLib.Environment.set_variable (key, val, true);
569+
570+ /* And also set it in the DBus activation environment so that any
571+ * indicator services pick it up. */
572+ try
573+ {
574+ var proxy = new GLib.DBusProxy.for_bus_sync (GLib.BusType.SESSION,
575+ GLib.DBusProxyFlags.NONE, null,
576+ "org.freedesktop.DBus",
577+ "/org/freedesktop/DBus",
578+ "org.freedesktop.DBus",
579+ null);
580+
581+ var builder = new GLib.VariantBuilder (GLib.VariantType.ARRAY);
582+ builder.add ("{ss}", key, val);
583+
584+ proxy.call ("UpdateActivationEnvironment", new GLib.Variant ("(a{ss})", builder), GLib.DBusCallFlags.NONE, -1, null);
585+ }
586+ catch (Error e)
587+ {
588+ warning ("Could not get set environment for indicators: %s", e.message);
589+ return;
590+ }
591+ }
592+
593+ private Gtk.Widget make_a11y_indicator ()
594+ {
595+ var a11y_item = new Gtk.MenuItem ();
596+ var hbox = new Gtk.HBox (false, 3);
597+ hbox.show ();
598+ a11y_item.add (hbox);
599+ var image = new Gtk.Image.from_file (Path.build_filename (Config.PKGDATADIR, "a11y.svg"));
600+ image.show ();
601+ hbox.add (image);
602+ a11y_item.show ();
603+ a11y_item.submenu = new Gtk.Menu ();
604+ var item = new Gtk.CheckMenuItem.with_label (_("Onscreen keyboard"));
605+ item.toggled.connect (keyboard_toggled_cb);
606+ item.show ();
607+ a11y_item.submenu.append (item);
608+ high_contrast_item = new Gtk.CheckMenuItem.with_label (_("High Contrast"));
609+ high_contrast_item.toggled.connect (high_contrast_toggled_cb);
610+ high_contrast_item.show ();
611+ a11y_item.submenu.append (high_contrast_item);
612+ item = new Gtk.CheckMenuItem.with_label (_("Screen Reader"));
613+ item.toggled.connect (screen_reader_toggled_cb);
614+ item.show ();
615+ a11y_item.submenu.append (item);
616+ return a11y_item;
617+ }
618+
619+ private void layout_toggled_cb (Gtk.CheckMenuItem item)
620+ {
621+ if (!item.active)
622+ return;
623+
624+ var layout = item.get_data<LightDM.Layout> ("unity-greeter-layout");
625+ if (layout == null)
626+ return;
627+
628+ keyboard_label.label = layout.short_description;
629+ LightDM.set_layout (layout);
630+ }
631+
632+ private static int cmp_layout (LightDM.Layout? a, LightDM.Layout? b)
633+ {
634+ if (a == null && b == null)
635+ return 0;
636+ else if (a == null)
637+ return 1;
638+ else if (b == null)
639+ return -1;
640+ else
641+ {
642+ /* Use a dumb, ascii comparison for now. If it turns out that some
643+ descriptions can be in unicode, we'll have to use libicu's collation
644+ algorithms. */
645+ return strcmp (a.description, b.description);
646+ }
647+ }
648+
649+ private Gtk.Widget make_keyboard_indicator ()
650+ {
651+ var keyboard_item = new Gtk.MenuItem ();
652+ var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 3);
653+ hbox.show ();
654+ keyboard_item.add (hbox);
655+ var image = new Gtk.Image.from_icon_name ("keyboard", Gtk.IconSize.LARGE_TOOLBAR);
656+ image.show ();
657+ hbox.add (image);
658+ keyboard_label = new Gtk.Label ("");
659+ keyboard_label.width_chars = 2;
660+ keyboard_label.show ();
661+ hbox.add (keyboard_label);
662+ keyboard_item.show ();
663+
664+ var submenu = new Gtk.Menu ();
665+ keyboard_item.set_submenu (submenu);
666+
667+ var layouts = LightDM.get_layouts ().copy ();
668+ layouts.sort (cmp_layout);
669+
670+ Gtk.RadioMenuItem? last_item = null;
671+ foreach (var layout in layouts)
672+ {
673+ var item = new Gtk.RadioMenuItem.with_label (last_item == null ? null : last_item.get_group (), layout.description);
674+ last_item = item;
675+
676+ item.show ();
677+
678+ /* LightDM does not change its layout list during its lifetime, so this is safe */
679+ item.set_data ("unity-greeter-layout", layout);
680+ layout.set_data ("unity-greeter-radio", item);
681+
682+ item.toggled.connect (layout_toggled_cb);
683+
684+ submenu.append (item);
685+ }
686+
687+ return keyboard_item;
688+ }
689+
690+ private void setup_indicators ()
691+ {
692+ /* Set indicators to run with reduced functionality */
693+ greeter_set_env ("INDICATOR_GREETER_MODE", "1");
694+
695+ /* Don't allow virtual file systems? */
696+ greeter_set_env ("GIO_USE_VFS", "local");
697+ greeter_set_env ("GVFS_DISABLE_FUSE", "1");
698+
699+ /* Hint to have gnome-settings-daemon run in greeter mode */
700+ greeter_set_env ("RUNNING_UNDER_GDM", "1");
701+
702+ var keyboard_item = make_keyboard_indicator ();
703+ insert (keyboard_item, (int) get_children ().length () - 1);
704+
705+ var a11y_item = make_a11y_indicator ();
706+ insert (a11y_item, (int) get_children ().length () - 1);
707+
708+ debug ("LANG=%s LANGUAGE=%s", Environment.get_variable ("LANG"), Environment.get_variable ("LANGUAGE"));
709+ string[] filenames = {"/usr/lib/indicators3/6/libsession.so",
710+ "/usr/lib/indicators3/6/libdatetime.so",
711+ "/usr/lib/indicators3/6/libpower.so",
712+ "/usr/lib/indicators3/6/libsoundmenu.so"};
713+ foreach (var filename in filenames)
714+ {
715+ var io = new Indicator.Object.from_file (filename);
716+ indicator_objects.append (io);
717+ io.entry_added.connect (indicator_added_cb);
718+ io.entry_removed.connect (indicator_removed_cb);
719+ foreach (var entry in io.get_entries ())
720+ indicator_added_cb (io, entry);
721+ }
722+ debug ("LANG=%s LANGUAGE=%s", Environment.get_variable ("LANG"), Environment.get_variable ("LANGUAGE"));
723+ }
724+
725+ private void keyboard_toggled_cb (Gtk.CheckMenuItem item)
726+ {
727+ /* FIXME: The below would be sufficient if gnome-session were running
728+ * to notice and run a screen keyboard in /etc/xdg/autostart... But
729+ * since we're not running gnome-session, we hardcode onboard here. */
730+ /* var settings = new Settings ("org.gnome.desktop.a11y.applications");*/
731+ /*settings.set_boolean ("screen-keyboard-enabled", item.active);*/
732+
733+ if (keyboard_window == null)
734+ {
735+ int id;
736+
737+ try
738+ {
739+ string[] argv;
740+ int onboard_stdout_fd;
741+
742+ Shell.parse_argv ("onboard --xid", out argv);
743+ Process.spawn_async_with_pipes (null,
744+ argv,
745+ null,
746+ SpawnFlags.SEARCH_PATH,
747+ null,
748+ out keyboard_pid,
749+ null,
750+ out onboard_stdout_fd,
751+ null);
752+ var f = FileStream.fdopen (onboard_stdout_fd, "r");
753+ var stdout_text = new char[1024];
754+ f.gets (stdout_text);
755+ id = int.parse ((string) stdout_text);
756+
757+ }
758+ catch (Error e)
759+ {
760+ warning ("Error setting up keyboard: %s", e.message);
761+ return;
762+ }
763+
764+ var keyboard_socket = new Gtk.Socket ();
765+ keyboard_socket.show ();
766+ keyboard_window = new Gtk.Window ();
767+ keyboard_window.accept_focus = false;
768+ keyboard_window.focus_on_map = false;
769+ keyboard_window.add (keyboard_socket);
770+ Gtk.socket_add_id (keyboard_socket, id);
771+
772+ /* Put keyboard at the bottom of the screen */
773+ var screen = get_screen ();
774+ var monitor = screen.get_monitor_at_window (get_window ());
775+ Gdk.Rectangle geom;
776+ screen.get_monitor_geometry (monitor, out geom);
777+ keyboard_window.move (geom.x, geom.y + geom.height - 200);
778+ keyboard_window.resize (geom.width, 200);
779+ }
780+
781+ keyboard_window.visible = item.active;
782+ }
783+
784+ private void high_contrast_toggled_cb (Gtk.CheckMenuItem item)
785+ {
786+ var settings = Gtk.Settings.get_default ();
787+ if (item.active)
788+ settings.set ("gtk-theme-name", "HighContrastInverse");
789+ else
790+ settings.set ("gtk-theme-name", default_theme_name);
791+ high_contrast = item.active;
792+ }
793+
794+ private void screen_reader_toggled_cb (Gtk.CheckMenuItem item)
795+ {
796+ /* FIXME: The below would be sufficient if gnome-session were running
797+ * to notice and run a screen reader in /etc/xdg/autostart... But
798+ * since we're not running gnome-session, we hardcode orca here.
799+ /*var settings = new Settings ("org.gnome.desktop.a11y.applications");*/
800+ /*settings.set_boolean ("screen-reader-enabled", item.active);*/
801+
802+ /* Hardcoded orca: */
803+ try
804+ {
805+ if (item.active)
806+ Process.spawn_command_line_async ("orca --replace --no-setup --disable=splash-window --disable=main-window");
807+ else
808+ Process.spawn_command_line_async ("orca --quit");
809+ }
810+ catch (Error e)
811+ {
812+ warning ("Failed to run Orca: %s", e.message);
813+ }
814+ }
815+
816+ private uint get_indicator_index (Indicator.Object object)
817+ {
818+ uint index = 0;
819+
820+ foreach (var io in indicator_objects)
821+ {
822+ if (io == object)
823+ return index;
824+ index++;
825+ }
826+
827+ return index;
828+ }
829+
830+ private Indicator.Object? get_indicator_object_from_entry (Indicator.ObjectEntry entry)
831+ {
832+ foreach (var io in indicator_objects)
833+ {
834+ foreach (var e in io.get_entries ())
835+ {
836+ if (e == entry)
837+ return io;
838+ }
839+ }
840+
841+ return null;
842+ }
843+
844+ private void indicator_added_cb (Indicator.Object object, Indicator.ObjectEntry entry)
845+ {
846+ var index = get_indicator_index (object);
847+ var pos = 0;
848+ foreach (var child in get_children ())
849+ {
850+ if (!(child is IndicatorMenuItem))
851+ break;
852+
853+ var menuitem = (IndicatorMenuItem) child;
854+ var child_object = get_indicator_object_from_entry (menuitem.entry);
855+ var child_index = get_indicator_index (child_object);
856+ if (child_index > index)
857+ break;
858+ pos++;
859+ }
860+
861+ debug ("Adding indicator object %p at position %d", entry, pos);
862+
863+ var menuitem = new IndicatorMenuItem (entry);
864+ insert (menuitem, pos);
865+ }
866+
867+ private void indicator_removed_cb (Indicator.Object object, Indicator.ObjectEntry entry)
868+ {
869+ debug ("Removing indicator object %p", entry);
870+
871+ foreach (var child in get_children ())
872+ {
873+ var menuitem = (IndicatorMenuItem) child;
874+ if (menuitem.entry == entry)
875+ {
876+ remove (child);
877+ return;
878+ }
879+ }
880+
881+ warning ("Indicator object %p not in menubar", entry);
882+ }
883+}
884
885=== modified file 'src/unity-greeter.vala'
886--- src/unity-greeter.vala 2012-01-12 13:17:02 +0000
887+++ src/unity-greeter.vala 2012-01-15 10:29:23 +0000
888@@ -120,11 +120,11 @@
889 }
890
891 user_list = new UserList ();
892+ user_list.background.draw_grid = get_config_value ("greeter", "draw-grid", "true") == "true";
893+ user_list.background.default_background = get_config_value ("greeter", "background", "#2C001E");
894+ user_list.background.set_logo (get_config_value ("greeter", "logo", Path.build_filename (Config.PKGDATADIR, "logo.png", null)));
895+ user_list.show ();
896 main_window.add (user_list);
897- user_list.set_logo (get_config_value ("greeter", "logo", Path.build_filename (Config.PKGDATADIR, "logo.png", null)));
898- user_list.draw_grid = get_config_value ("greeter", "draw-grid", "true") == "true";
899- user_list.default_background = get_config_value ("greeter", "background", "#2C001E");
900- user_list.show ();
901
902 foreach (var session in LightDM.get_sessions ())
903 {
904@@ -295,12 +295,20 @@
905 user_list.show_prompt (text, type == LightDM.PromptType.SECRET);
906 }
907
908- private void background_loaded_cb (Background background)
909+ private void background_loaded_cb (ParamSpec pspec)
910+ {
911+ if (user_list.background.alpha == 1.0)
912+ {
913+ user_list.background.notify["alpha"].disconnect (background_loaded_cb);
914+ start_session ();
915+ }
916+ }
917+
918+ private void start_session ()
919 {
920 /* Set the background */
921 var c = new Cairo.Context (background_surface);
922- c.set_source (background.pattern);
923- c.paint ();
924+ user_list.background.draw (c);
925 c = null;
926 refresh_background (Gdk.Screen.get_default ());
927
928@@ -331,14 +339,12 @@
929 if (prompted)
930 {
931 user_list.login_complete ();
932- if (test_mode)
933- user_list.login_complete ();
934- else
935+ if (!test_mode)
936 {
937- var background = user_list.get_background ();
938- background.loaded.connect (background_loaded_cb);
939- if (background.load ())
940- background_loaded_cb (background);
941+ if (user_list.background.alpha == 1.0)
942+ start_session ();
943+ else
944+ user_list.background.notify["alpha"].connect (background_loaded_cb);
945 }
946 }
947 else
948
949=== modified file 'src/user-list.vala'
950--- src/user-list.vala 2012-01-12 13:17:02 +0000
951+++ src/user-list.vala 2012-01-15 10:29:23 +0000
952@@ -1,6 +1,6 @@
953 /* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 4 -*-
954 *
955- * Copyright (C) 2011 Canonical Ltd
956+ * Copyright (C) 2011,2012 Canonical Ltd
957 *
958 * This program is free software: you can redistribute it and/or modify
959 * it under the terms of the GNU General Public License version 3 as
960@@ -14,11 +14,10 @@
961 * You should have received a copy of the GNU General Public License
962 * along with this program. If not, see <http://www.gnu.org/licenses/>.
963 *
964- * Authored by: Robert Ancell <robert.ancell@canonical.com>
965+ * Authors: Robert Ancell <robert.ancell@canonical.com>
966+ * Michael Terry <michael.terry@canonical.com>
967 */
968
969-private Cairo.Surface? logo_surface = null;
970-
971 private int get_grid_offset (int size)
972 {
973 return (int) (size % grid_size) / 2;
974@@ -42,222 +41,17 @@
975 public LightDM.Layout keyboard_layout;
976 }
977
978-private class IndicatorMenuItem : Gtk.MenuItem
979-{
980- public unowned Indicator.ObjectEntry entry;
981- private Gtk.HBox hbox;
982-
983- public IndicatorMenuItem (Indicator.ObjectEntry entry)
984- {
985- this.entry = entry;
986- this.hbox = new Gtk.HBox (false, 3);
987- this.add (this.hbox);
988- this.hbox.show ();
989-
990- if (entry.label != null)
991- {
992- entry.label.show.connect (this.visibility_changed_cb);
993- entry.label.hide.connect (this.visibility_changed_cb);
994- hbox.pack_start (entry.label, false, false, 0);
995- }
996- if (entry.image != null)
997- {
998- entry.image.show.connect (visibility_changed_cb);
999- entry.image.hide.connect (visibility_changed_cb);
1000- hbox.pack_start (entry.image, false, false, 0);
1001- }
1002- if (entry.accessible_desc != null)
1003- get_accessible ().set_name (entry.accessible_desc);
1004- if (entry.menu != null)
1005- submenu = entry.menu;
1006-
1007- if (has_visible_child ())
1008- show();
1009- }
1010-
1011- public bool has_visible_child ()
1012- {
1013- return (entry.image != null && entry.image.get_visible ()) ||
1014- (entry.label != null && entry.label.get_visible ());
1015- }
1016-
1017- public void visibility_changed_cb (Gtk.Widget widget)
1018- {
1019- visible = has_visible_child ();
1020- }
1021-}
1022-
1023 private class SessionMenuItem : Gtk.RadioMenuItem
1024 {
1025 public string session_name;
1026 }
1027
1028-public class Background
1029-{
1030- public string filename {get; private set;}
1031- private int width;
1032- private int height;
1033- private bool draw_grid;
1034- private unowned Thread<Cairo.Pattern?> thread;
1035- private bool finished;
1036-
1037- public Cairo.Pattern? pattern = null;
1038-
1039- public signal void loaded ();
1040-
1041- public Background (string filename, int width, int height, bool draw_grid)
1042- {
1043- this.filename = filename;
1044- this.width = width;
1045- this.height = height;
1046- this.draw_grid = draw_grid;
1047- }
1048-
1049- public bool load ()
1050- {
1051- /* Already loaded */
1052- if (finished)
1053- return true;
1054-
1055- /* Currently loading */
1056- if (thread != null)
1057- return false;
1058-
1059- debug ("Making background %s at %dx%d", filename, width, height);
1060- try
1061- {
1062- thread = Thread<Cairo.Pattern?>.create<Cairo.Pattern?> (render, true);
1063- }
1064- catch (ThreadError e)
1065- {
1066- finished = true;
1067- return true;
1068- }
1069-
1070- return false;
1071- }
1072-
1073- private bool ready_cb ()
1074- {
1075- debug ("Render of background %s at %dx%d complete", filename, width, height);
1076-
1077- pattern = thread.join ();
1078- thread = null;
1079- finished = true;
1080-
1081- loaded ();
1082-
1083- return false;
1084- }
1085-
1086- private Cairo.Pattern? render ()
1087- {
1088- Gdk.Color color;
1089- Gdk.Pixbuf orig_image = null;
1090- if (!Gdk.Color.parse (filename, out color))
1091- {
1092- try
1093- {
1094- orig_image = new Gdk.Pixbuf.from_file (filename);
1095- }
1096- catch (Error e)
1097- {
1098- debug ("Error loading background: %s", e.message);
1099- Gdk.threads_add_idle (ready_cb);
1100- return null;
1101- }
1102- }
1103-
1104- Gdk.Pixbuf? image = null;
1105- if (orig_image != null)
1106- {
1107- var target_aspect = (double) width / height;
1108- var aspect = (double) orig_image.width / orig_image.height;
1109- double scale, offset_x = 0, offset_y = 0;
1110- if (aspect > target_aspect)
1111- {
1112- /* Fit height and trim sides */
1113- scale = (double) height / orig_image.height;
1114- offset_x = (orig_image.width * scale - width) / 2;
1115- }
1116- else
1117- {
1118- /* Fit width and trim top and bottom */
1119- scale = (double) width / orig_image.width;
1120- offset_y = (orig_image.height * scale - height) / 2;
1121- }
1122-
1123- image = new Gdk.Pixbuf (orig_image.colorspace, orig_image.has_alpha, orig_image.bits_per_sample, width, height);
1124- orig_image.scale (image, 0, 0, width, height, -offset_x, -offset_y, scale, scale, Gdk.InterpType.BILINEAR);
1125- }
1126-
1127- var grid_x_offset = get_grid_offset (width);
1128- var grid_y_offset = get_grid_offset (height);
1129-
1130- /* Overlay grid */
1131- var overlay_surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, grid_size, grid_size);
1132- var oc = new Cairo.Context (overlay_surface);
1133- oc.rectangle (0, 0, 1, 1);
1134- oc.rectangle (grid_size - 1, 0, 1, 1);
1135- oc.rectangle (0, grid_size - 1, 1, 1);
1136- oc.rectangle (grid_size - 1, grid_size - 1, 1, 1);
1137- oc.set_source_rgba (1.0, 1.0, 1.0, 0.25);
1138- oc.fill ();
1139- var overlay = new Cairo.Pattern.for_surface (overlay_surface);
1140- var matrix = Cairo.Matrix.identity ();
1141- matrix.translate (-grid_x_offset, -grid_y_offset);
1142- overlay.set_matrix (matrix);
1143- overlay.set_extend (Cairo.Extend.REPEAT);
1144-
1145- /* Create background */
1146- var surface = new Cairo.ImageSurface (Cairo.Format.RGB24, width, height);
1147- var bc = new Cairo.Context (surface);
1148- if (image != null)
1149- Gdk.cairo_set_source_pixbuf (bc, image, 0, 0);
1150- else
1151- Gdk.cairo_set_source_color (bc, color);
1152- bc.paint ();
1153-
1154- /* Draw overlay */
1155- if (draw_grid)
1156- {
1157- bc.set_source (overlay);
1158- bc.rectangle (0, 0, width, height);
1159- bc.fill ();
1160- }
1161-
1162- /* Draw logo */
1163- if (logo_surface != null)
1164- {
1165- bc.save ();
1166- var y = (int) (height / grid_size - 2) * grid_size + grid_y_offset;
1167- bc.translate (grid_x_offset, y);
1168- bc.set_source_surface (logo_surface, 0, 0);
1169- bc.paint_with_alpha (0.5);
1170- bc.restore ();
1171- }
1172-
1173- var pattern = new Cairo.Pattern.for_surface (surface);
1174- pattern.set_extend (Cairo.Extend.REPEAT);
1175-
1176- Gdk.threads_add_idle (ready_cb);
1177-
1178- return pattern;
1179- }
1180-}
1181-
1182 public class UserList : Gtk.Container
1183 {
1184- private string default_theme_name;
1185-
1186 private int frame_count = 0;
1187-
1188- private HashTable<string, Background> backgrounds;
1189- private Background background;
1190- private Background old_background;
1191+
1192+ public Background background;
1193 private uint change_background_timeout = 0;
1194- public string default_background = "#2C001E";
1195- public bool draw_grid = true;
1196
1197 private List<UserEntry> entries = null;
1198 private UserEntry? selected_entry = null;
1199@@ -269,20 +63,9 @@
1200
1201 private AnimateTimer scroll_timer;
1202
1203- private AnimateTimer background_timer;
1204- private double background_alpha;
1205-
1206 private List<Gtk.Widget> children;
1207
1208- private Gtk.MenuBar menubar;
1209- private Cairo.Context menubar_cairo_context;
1210- public List<Indicator.Object> indicator_objects;
1211- private Gtk.CheckMenuItem high_contrast_item;
1212-
1213- private Gtk.Label keyboard_label = null;
1214-
1215- private Pid keyboard_pid = 0;
1216- private Gtk.Window? keyboard_window = null;
1217+ private MenuBar menubar;
1218
1219 private string? error;
1220 private string? message;
1221@@ -356,30 +139,17 @@
1222 public UserList ()
1223 {
1224 can_focus = false;
1225- background_alpha = 1.0;
1226-
1227- Gtk.Settings.get_default ().get ("gtk-theme-name", out default_theme_name);
1228-
1229- menubar = new Gtk.MenuBar ();
1230- menubar.draw.connect_after (menubar_draw_cb);
1231- menubar.pack_direction = Gtk.PackDirection.RTL;
1232+
1233+ background = new Background ();
1234+ background.show ();
1235+ add (background);
1236+
1237+ menubar = new MenuBar (background);
1238+ menubar.notify["high-contrast"].connect (() => {change_background ();});
1239 menubar.show ();
1240 add (menubar);
1241
1242- var label = new Gtk.Label (Posix.utsname ().nodename);
1243- label.show ();
1244- var hostname_item = new Gtk.MenuItem ();
1245- hostname_item.add (label);
1246- hostname_item.sensitive = false;
1247- hostname_item.right_justified = true;
1248- hostname_item.show ();
1249- menubar.append (hostname_item);
1250-
1251- /* Hack to get a label showing on the menubar */
1252- label.ensure_style ();
1253- label.modify_fg (Gtk.StateType.INSENSITIVE, label.get_style ().fg[Gtk.StateType.NORMAL]);
1254-
1255- login_box = new DashBox ();
1256+ login_box = new DashBox (background);
1257 login_box.show ();
1258 add (login_box);
1259
1260@@ -396,7 +166,7 @@
1261 add (prompt_entry);
1262
1263 login_button = new Gtk.Button ();
1264- label = new Gtk.Label ("<span font_size=\"large\">%s</span>".printf (/* 'Log In' here is the button for logging in. */
1265+ var label = new Gtk.Label ("<span font_size=\"large\">%s</span>".printf (/* 'Log In' here is the button for logging in. */
1266 _("Log In")));
1267 label.use_markup = true;
1268 label.show ();
1269@@ -424,275 +194,8 @@
1270
1271 options_menu = new Gtk.Menu ();
1272
1273- backgrounds = new HashTable<string?, Background> (str_hash, str_equal);
1274-
1275 scroll_timer = new AnimateTimer (AnimateTimer.Speed.FAST);
1276 scroll_timer.animate.connect (scroll_animate_cb);
1277- background_timer = new AnimateTimer (AnimateTimer.Speed.INSTANT);
1278- background_timer.animate.connect (background_animate_cb);
1279-
1280- setup_indicators ();
1281- }
1282-
1283- public void set_logo (string logo_path)
1284- {
1285- if (FileUtils.test (logo_path, FileTest.EXISTS))
1286- {
1287- debug ("Using logo %s", logo_path);
1288- logo_surface = new Cairo.ImageSurface.from_png (logo_path);
1289- }
1290- else
1291- {
1292- debug ("Can't use logo %s, it does not exist", logo_path);
1293- logo_surface = null;
1294- }
1295- }
1296-
1297- private void draw_child_cb (Gtk.Widget child)
1298- {
1299- menubar.propagate_draw (child, menubar_cairo_context);
1300- }
1301-
1302- private bool menubar_draw_cb (Cairo.Context c)
1303- {
1304- draw_background (c);
1305-
1306- c.set_source_rgb (0, 0, 0);
1307- c.paint_with_alpha (0.5);
1308-
1309- menubar_cairo_context = c;
1310- menubar.forall (draw_child_cb);
1311-
1312- return false;
1313- }
1314-
1315- void greeter_set_env (string key, string val)
1316- {
1317- GLib.Environment.set_variable (key, val, true);
1318-
1319- /* And also set it in the DBus activation environment so that any
1320- * indicator services pick it up. */
1321- try
1322- {
1323- var proxy = new GLib.DBusProxy.for_bus_sync (GLib.BusType.SESSION,
1324- GLib.DBusProxyFlags.NONE, null,
1325- "org.freedesktop.DBus",
1326- "/org/freedesktop/DBus",
1327- "org.freedesktop.DBus",
1328- null);
1329-
1330- var builder = new GLib.VariantBuilder (GLib.VariantType.ARRAY);
1331- builder.add ("{ss}", key, val);
1332-
1333- proxy.call ("UpdateActivationEnvironment", new GLib.Variant ("(a{ss})", builder), GLib.DBusCallFlags.NONE, -1, null);
1334- }
1335- catch (Error e)
1336- {
1337- warning ("Could not get set environment for indicators: %s", e.message);
1338- return;
1339- }
1340- }
1341-
1342- Gtk.Widget make_a11y_indicator ()
1343- {
1344- var a11y_item = new Gtk.MenuItem ();
1345- var hbox = new Gtk.HBox (false, 3);
1346- hbox.show ();
1347- a11y_item.add (hbox);
1348- var image = new Gtk.Image.from_file (Path.build_filename (Config.PKGDATADIR, "a11y.svg"));
1349- image.show ();
1350- hbox.add (image);
1351- a11y_item.show ();
1352- a11y_item.submenu = new Gtk.Menu ();
1353- var item = new Gtk.CheckMenuItem.with_label (_("Onscreen keyboard"));
1354- item.toggled.connect (keyboard_toggled_cb);
1355- item.show ();
1356- a11y_item.submenu.append (item);
1357- high_contrast_item = new Gtk.CheckMenuItem.with_label (_("High Contrast"));
1358- high_contrast_item.toggled.connect (high_contrast_toggled_cb);
1359- high_contrast_item.show ();
1360- a11y_item.submenu.append (high_contrast_item);
1361- item = new Gtk.CheckMenuItem.with_label (_("Screen Reader"));
1362- item.toggled.connect (screen_reader_toggled_cb);
1363- item.show ();
1364- a11y_item.submenu.append (item);
1365- return a11y_item;
1366- }
1367-
1368- void set_layout (LightDM.Layout? layout)
1369- {
1370- if (layout == null)
1371- layout = LightDM.get_layout (); /* default layout */
1372-
1373- var item = layout.get_data<Gtk.RadioMenuItem> ("unity-greeter-radio");
1374- if (item != null)
1375- item.active = true; /* will trigger callback to do rest of work */
1376- }
1377-
1378- void layout_toggled_cb (Gtk.CheckMenuItem item)
1379- {
1380- if (!item.active)
1381- return;
1382-
1383- var layout = item.get_data<LightDM.Layout> ("unity-greeter-layout");
1384- if (layout == null)
1385- return;
1386-
1387- keyboard_label.label = layout.short_description;
1388- LightDM.set_layout (layout);
1389- }
1390-
1391- static int cmp_layout (LightDM.Layout? a, LightDM.Layout? b)
1392- {
1393- if (a == null && b == null)
1394- return 0;
1395- else if (a == null)
1396- return 1;
1397- else if (b == null)
1398- return -1;
1399- else
1400- {
1401- /* Use a dumb, ascii comparison for now. If it turns out that some
1402- descriptions can be in unicode, we'll have to use libicu's collation
1403- algorithms. */
1404- return strcmp (a.description, b.description);
1405- }
1406- }
1407-
1408- Gtk.Widget make_keyboard_indicator ()
1409- {
1410- var keyboard_item = new Gtk.MenuItem ();
1411- var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 3);
1412- hbox.show ();
1413- keyboard_item.add (hbox);
1414- var image = new Gtk.Image.from_icon_name ("keyboard", Gtk.IconSize.LARGE_TOOLBAR);
1415- image.show ();
1416- hbox.add (image);
1417- keyboard_label = new Gtk.Label ("");
1418- keyboard_label.width_chars = 2;
1419- keyboard_label.show ();
1420- hbox.add (keyboard_label);
1421- keyboard_item.show ();
1422-
1423- var submenu = new Gtk.Menu ();
1424- keyboard_item.set_submenu (submenu);
1425-
1426- var layouts = LightDM.get_layouts ().copy ();
1427- layouts.sort (cmp_layout);
1428-
1429- Gtk.RadioMenuItem? last_item = null;
1430- foreach (var layout in layouts)
1431- {
1432- var item = new Gtk.RadioMenuItem.with_label (last_item == null ? null : last_item.get_group (), layout.description);
1433- last_item = item;
1434-
1435- item.show ();
1436-
1437- /* LightDM does not change its layout list during its lifetime, so this is safe */
1438- item.set_data ("unity-greeter-layout", layout);
1439- layout.set_data ("unity-greeter-radio", item);
1440-
1441- item.toggled.connect (layout_toggled_cb);
1442-
1443- submenu.append (item);
1444- }
1445-
1446- return keyboard_item;
1447- }
1448-
1449- void setup_indicators ()
1450- {
1451- /* Set indicators to run with reduced functionality */
1452- greeter_set_env ("INDICATOR_GREETER_MODE", "1");
1453-
1454- /* Don't allow virtual file systems? */
1455- greeter_set_env ("GIO_USE_VFS", "local");
1456- greeter_set_env ("GVFS_DISABLE_FUSE", "1");
1457-
1458- /* Hint to have gnome-settings-daemon run in greeter mode */
1459- greeter_set_env ("RUNNING_UNDER_GDM", "1");
1460-
1461- var keyboard_item = make_keyboard_indicator ();
1462- menubar.insert (keyboard_item, (int) menubar.get_children ().length () - 1);
1463-
1464- var a11y_item = make_a11y_indicator ();
1465- menubar.insert (a11y_item, (int) menubar.get_children ().length () - 1);
1466-
1467- debug ("LANG=%s LANGUAGE=%s", Environment.get_variable ("LANG"), Environment.get_variable ("LANGUAGE"));
1468- string[] filenames = {"/usr/lib/indicators3/6/libsession.so",
1469- "/usr/lib/indicators3/6/libdatetime.so",
1470- "/usr/lib/indicators3/6/libpower.so",
1471- "/usr/lib/indicators3/6/libsoundmenu.so"};
1472- foreach (var filename in filenames)
1473- {
1474- var io = new Indicator.Object.from_file (filename);
1475- indicator_objects.append (io);
1476- io.entry_added.connect (indicator_added_cb);
1477- io.entry_removed.connect (indicator_removed_cb);
1478- foreach (var entry in io.get_entries ())
1479- indicator_added_cb (io, entry);
1480- }
1481- debug ("LANG=%s LANGUAGE=%s", Environment.get_variable ("LANG"), Environment.get_variable ("LANGUAGE"));
1482- }
1483-
1484- private void keyboard_toggled_cb (Gtk.CheckMenuItem item)
1485- {
1486- /* FIXME: The below would be sufficient if gnome-session were running
1487- * to notice and run a screen keyboard in /etc/xdg/autostart... But
1488- * since we're not running gnome-session, we hardcode onboard here. */
1489- /* var settings = new Settings ("org.gnome.desktop.a11y.applications");*/
1490- /*settings.set_boolean ("screen-keyboard-enabled", item.active);*/
1491-
1492- if (keyboard_window == null)
1493- {
1494- int id;
1495-
1496- try
1497- {
1498- string[] argv;
1499- int onboard_stdout_fd;
1500-
1501- Shell.parse_argv ("onboard --xid", out argv);
1502- Process.spawn_async_with_pipes (null,
1503- argv,
1504- null,
1505- SpawnFlags.SEARCH_PATH,
1506- null,
1507- out keyboard_pid,
1508- null,
1509- out onboard_stdout_fd,
1510- null);
1511- var f = FileStream.fdopen (onboard_stdout_fd, "r");
1512- var stdout_text = new char[1024];
1513- f.gets (stdout_text);
1514- id = int.parse ((string) stdout_text);
1515-
1516- }
1517- catch (Error e)
1518- {
1519- warning ("Error setting up keyboard: %s", e.message);
1520- return;
1521- }
1522-
1523- var keyboard_socket = new Gtk.Socket ();
1524- keyboard_socket.show ();
1525- keyboard_window = new Gtk.Window ();
1526- keyboard_window.accept_focus = false;
1527- keyboard_window.focus_on_map = false;
1528- keyboard_window.add (keyboard_socket);
1529- Gtk.socket_add_id (keyboard_socket, id);
1530-
1531- /* Put keyboard at the bottom of the screen */
1532- keyboard_window.move (0, get_allocated_height () - 200);
1533- keyboard_window.resize (get_allocated_width (), 200);
1534- }
1535-
1536- keyboard_window.visible = item.active;
1537- }
1538-
1539- private void redraw_background ()
1540- {
1541- queue_draw ();
1542 }
1543
1544 private void redraw_user_list ()
1545@@ -707,39 +210,6 @@
1546 queue_draw_area (box_x, box_y, box_width * grid_size, box_height * grid_size);
1547 }
1548
1549- private void high_contrast_toggled_cb (Gtk.CheckMenuItem item)
1550- {
1551- var settings = Gtk.Settings.get_default ();
1552- if (item.active)
1553- settings.set ("gtk-theme-name", "HighContrastInverse");
1554- else
1555- settings.set ("gtk-theme-name", default_theme_name);
1556- change_background ();
1557- redraw_background ();
1558- }
1559-
1560- private void screen_reader_toggled_cb (Gtk.CheckMenuItem item)
1561- {
1562- /* FIXME: The below would be sufficient if gnome-session were running
1563- * to notice and run a screen reader in /etc/xdg/autostart... But
1564- * since we're not running gnome-session, we hardcode orca here.
1565- /*var settings = new Settings ("org.gnome.desktop.a11y.applications");*/
1566- /*settings.set_boolean ("screen-reader-enabled", item.active);*/
1567-
1568- /* Hardcoded orca: */
1569- try
1570- {
1571- if (item.active)
1572- Process.spawn_command_line_async ("orca --replace --no-setup --disable=splash-window --disable=main-window");
1573- else
1574- Process.spawn_command_line_async ("orca --quit");
1575- }
1576- catch (Error e)
1577- {
1578- warning ("Failed to run Orca: %s", e.message);
1579- }
1580- }
1581-
1582 public void show_message (string text, bool error = false)
1583 {
1584 message = text;
1585@@ -807,74 +277,6 @@
1586 redraw_login_box ();
1587 }
1588
1589- private uint get_indicator_index (Indicator.Object object)
1590- {
1591- uint index = 0;
1592-
1593- foreach (var io in indicator_objects)
1594- {
1595- if (io == object)
1596- return index;
1597- index++;
1598- }
1599-
1600- return index;
1601- }
1602-
1603- private Indicator.Object? get_indicator_object_from_entry (Indicator.ObjectEntry entry)
1604- {
1605- foreach (var io in indicator_objects)
1606- {
1607- foreach (var e in io.get_entries ())
1608- {
1609- if (e == entry)
1610- return io;
1611- }
1612- }
1613-
1614- return null;
1615- }
1616-
1617- private void indicator_added_cb (Indicator.Object object, Indicator.ObjectEntry entry)
1618- {
1619- var index = get_indicator_index (object);
1620- var pos = 0;
1621- foreach (var child in menubar.get_children ())
1622- {
1623- if (!(child is IndicatorMenuItem))
1624- break;
1625-
1626- var menuitem = (IndicatorMenuItem) child;
1627- var child_object = get_indicator_object_from_entry (menuitem.entry);
1628- var child_index = get_indicator_index (child_object);
1629- if (child_index > index)
1630- break;
1631- pos++;
1632- }
1633-
1634- debug ("Adding indicator object %p at position %d", entry, pos);
1635-
1636- var menuitem = new IndicatorMenuItem (entry);
1637- menubar.insert (menuitem, pos);
1638- }
1639-
1640- private void indicator_removed_cb (Indicator.Object object, Indicator.ObjectEntry entry)
1641- {
1642- debug ("Removing indicator object %p", entry);
1643-
1644- foreach (var child in menubar.get_children ())
1645- {
1646- var menuitem = (IndicatorMenuItem) child;
1647- if (menuitem.entry == entry)
1648- {
1649- menubar.remove (child);
1650- return;
1651- }
1652- }
1653-
1654- warning ("Indicator object %p not in menubar", entry);
1655- }
1656-
1657 public void add_session (string name, string label)
1658 {
1659 var item = new SessionMenuItem ();
1660@@ -908,7 +310,7 @@
1661 }
1662 e.layout = create_pango_layout (label);
1663 e.layout.set_font_description (Pango.FontDescription.from_string ("Ubuntu 16"));
1664- e.background = background != null ? background : default_background;
1665+ e.background = background;
1666 e.keyboard_layout = keyboard_layout;
1667 e.is_active = is_active;
1668
1669@@ -1003,33 +405,15 @@
1670 finished_animating ();
1671 }
1672
1673- private void background_animate_cb (double progress)
1674- {
1675- background_alpha = progress;
1676-
1677- redraw_background ();
1678-
1679- /* Stop when we get there */
1680- if (background_alpha >= 1.0)
1681- old_background = background;
1682- }
1683-
1684 private bool change_background_timeout_cb ()
1685 {
1686- Background new_background;
1687- if (high_contrast_item.active)
1688- new_background = make_background (default_background);
1689+ string new_background_file;
1690+ if (menubar.high_contrast)
1691+ new_background_file = null;
1692 else
1693- new_background = make_background (selected_entry.background);
1694+ new_background_file = selected_entry.background;
1695
1696- if (background != new_background)
1697- {
1698- if (frame_count > 0)
1699- background_alpha = 0.0;
1700- old_background = background;
1701- background = new_background;
1702- redraw_background ();
1703- }
1704+ background.current_background = new_background_file;
1705
1706 change_background_timeout = 0;
1707 return false;
1708@@ -1082,7 +466,7 @@
1709 user_selected (selected_entry.name);
1710 }
1711
1712- set_layout (selected_entry.keyboard_layout);
1713+ menubar.set_layout (selected_entry.keyboard_layout);
1714 }
1715 }
1716
1717@@ -1108,7 +492,6 @@
1718
1719 public override void realize ()
1720 {
1721- background = old_background = make_background (default_background);
1722 change_background ();
1723
1724 set_realized (true);
1725@@ -1161,15 +544,18 @@
1726 if (resized)
1727 debug ("Resized to %dx%d", get_allocated_width (), get_allocated_height ());
1728
1729- Gtk.Requisition natural_size;
1730- menubar.get_preferred_size (null, out natural_size);
1731 var child_allocation = Gtk.Allocation ();
1732- natural_size.height = 32;
1733- natural_size.width = get_allocated_width ();
1734- child_allocation.x = 0;
1735- child_allocation.y = 0;
1736- child_allocation.width = natural_size.width;
1737- child_allocation.height = natural_size.height;
1738+
1739+ child_allocation.x = 0;
1740+ child_allocation.y = 0;
1741+ child_allocation.width = allocation.width;
1742+ child_allocation.height = allocation.height;
1743+ background.size_allocate (child_allocation);
1744+
1745+ child_allocation.x = 0;
1746+ child_allocation.y = 0;
1747+ child_allocation.width = allocation.width;
1748+ child_allocation.height = 32;
1749 menubar.size_allocate (child_allocation);
1750
1751 child_allocation.x = box_x + 6;
1752@@ -1191,21 +577,13 @@
1753 child_allocation.width = grid_size;
1754 child_allocation.height = grid_size;
1755 options_button.size_allocate (child_allocation);
1756-
1757- /* Regenerate backgrounds */
1758- if (resized)
1759- {
1760- debug ("Regenerating backgrounds");
1761- backgrounds.remove_all ();
1762- change_background ();
1763- }
1764 }
1765
1766 private void draw_entry (Cairo.Context c, UserEntry entry, double alpha = 0.5, bool in_box = false)
1767 {
1768 c.save ();
1769
1770- if (high_contrast_item.active || in_box)
1771+ if (menubar.high_contrast || in_box)
1772 alpha = 1.0;
1773
1774 if (entry.is_active)
1775@@ -1257,72 +635,6 @@
1776 }
1777 }
1778
1779- private void background_loaded_cb (Background b)
1780- {
1781- change_background ();
1782- redraw_background ();
1783- }
1784-
1785- private Background make_background (string filename)
1786- {
1787- var b = backgrounds.lookup (filename);
1788- if (b == null)
1789- {
1790- b = new Background (filename, get_allocated_width (), get_allocated_height (), this.draw_grid);
1791- b.loaded.connect (background_loaded_cb);
1792- backgrounds.insert (filename, b);
1793- }
1794-
1795- return b;
1796- }
1797-
1798- public Background get_background ()
1799- {
1800- if (selected_entry != null)
1801- return make_background (selected_entry.background);
1802- else
1803- return make_background (default_background);
1804- }
1805-
1806- private void draw_background (Cairo.Context c)
1807- {
1808- /* Test whether we ran into an error loading this background */
1809- if (background.load () && background.pattern == null)
1810- {
1811- /* We couldn't load it, so swap it out for the default background
1812- and remember that choice */
1813- var new_background = make_background (default_background);
1814- backgrounds.insert (background.filename, new_background);
1815- if (old_background == background)
1816- old_background = new_background;
1817- background = new_background;
1818- }
1819-
1820- /* Fade to this background when loaded */
1821- if (background.load () && background != old_background && !background_timer.is_running)
1822- background_timer.reset ();
1823-
1824- c.set_source_rgb (0x2C, 0x00, 0x1E);
1825- var old_not_loaded = false;
1826-
1827- /* Draw old background */
1828- if (background_alpha < 1.0)
1829- {
1830- if (old_background.load ())
1831- c.set_source (old_background.pattern);
1832- else
1833- old_not_loaded = true;
1834- c.paint ();
1835- }
1836-
1837- /* Draw new background */
1838- if (background.load () && background_alpha > 0.0)
1839- {
1840- c.set_source (background.pattern);
1841- c.paint_with_alpha (old_not_loaded ? 1.0 : background_alpha);
1842- }
1843- }
1844-
1845 private void draw_entry_at_position (Cairo.Context c, UserEntry entry,
1846 double position, bool in_box = false)
1847 {
1848@@ -1344,8 +656,6 @@
1849
1850 frame_count++;
1851
1852- draw_background (c);
1853-
1854 foreach (var child in children)
1855 propagate_draw (child, c);
1856
1857@@ -1548,14 +858,4 @@
1858
1859 return false;
1860 }
1861-
1862- public override void unrealize ()
1863- {
1864- if (keyboard_pid != 0)
1865- {
1866- Posix.kill (keyboard_pid, Posix.SIGKILL);
1867- int status;
1868- Posix.waitpid (keyboard_pid, out status, 0);
1869- }
1870- }
1871 }

Subscribers

People subscribed via source and target branches