summaryrefslogtreecommitdiff
path: root/gtk/controller/foreign-menu.vala
diff options
context:
space:
mode:
Diffstat (limited to 'gtk/controller/foreign-menu.vala')
-rw-r--r--gtk/controller/foreign-menu.vala207
1 files changed, 207 insertions, 0 deletions
diff --git a/gtk/controller/foreign-menu.vala b/gtk/controller/foreign-menu.vala
new file mode 100644
index 0000000..677e2ad
--- /dev/null
+++ b/gtk/controller/foreign-menu.vala
@@ -0,0 +1,207 @@
+// Copyright (C) 2012 Red Hat, Inc.
+
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+using Custom;
+
+namespace SpiceCtrl {
+
+public class ForeignMenu: Object {
+
+ public Menu menu { get; private set; }
+ public string title { get; private set; }
+
+ private int nclients;
+ private List<IOStream> clients;
+
+ public ForeignMenu() {
+ menu = new Menu ();
+ }
+
+ public void menu_item_click_msg (int32 item_id) {
+ debug ("clicked id: %d".printf (item_id));
+
+ var msg = SpiceProtocol.ForeignMenu.Event ();
+ msg.base.size = (uint32)sizeof (SpiceProtocol.ForeignMenu.Event);
+ msg.base.id = SpiceProtocol.ForeignMenu.MsgId.ITEM_EVENT;
+ msg.id = item_id;
+ msg.action = SpiceProtocol.ForeignMenu.EventType.CLICK;
+
+ unowned uint8[] p = ((uint8[])(&msg))[0:msg.base.size];
+ send_msg (p);
+ }
+
+ public void menu_item_checked_msg (int32 item_id, bool checked = true) {
+ debug ("%schecked id: %d".printf (checked ? "" : "un", item_id));
+
+ var msg = SpiceProtocol.ForeignMenu.Event ();
+ msg.base.size = (uint32)sizeof (SpiceProtocol.ForeignMenu.Event);
+ msg.base.id = SpiceProtocol.ForeignMenu.MsgId.ITEM_EVENT;
+ msg.id = item_id;
+ msg.action = checked ?
+ SpiceProtocol.ForeignMenu.EventType.CHECKED :
+ SpiceProtocol.ForeignMenu.EventType.UNCHECKED;
+
+ unowned uint8[] p = ((uint8[])(&msg))[0:msg.base.size];
+ send_msg (p);
+ }
+
+ public void app_activated_msg (bool activated = true) {
+ var msg = SpiceProtocol.ForeignMenu.Msg ();
+ msg.size = (uint32)sizeof (SpiceProtocol.ForeignMenu.Event);
+ msg.id = activated ?
+ SpiceProtocol.ForeignMenu.MsgId.APP_ACTIVATED :
+ SpiceProtocol.ForeignMenu.MsgId.APP_DEACTIVATED;
+
+ unowned uint8[] p = ((uint8[])(&msg))[0:msg.size];
+ send_msg (p);
+ }
+
+ public async bool send_msg (uint8[] p) throws GLib.Error {
+ // vala FIXME: pass Controller.Msg instead
+ // vala doesn't keep reference on the struct in async methods
+ // it copies only base, which is not enough to transmit the whole
+ // message.
+ try {
+ foreach (var c in clients)
+ yield c.output_stream.write_async (p);
+ } catch (GLib.Error e) {
+ warning (e.message);
+ }
+
+ return true;
+ }
+
+ SpiceProtocol.Controller.MenuFlags get_menu_flags (uint32 type) {
+ SpiceProtocol.Controller.MenuFlags flags = 0;
+
+ if ((SpiceProtocol.ForeignMenu.MenuFlags.CHECKED & type) != 0)
+ flags |= SpiceProtocol.Controller.MenuFlags.CHECKED;
+ if ((SpiceProtocol.ForeignMenu.MenuFlags.DIM & type) != 0)
+ flags |= SpiceProtocol.Controller.MenuFlags.GRAYED;
+
+ return flags;
+ }
+
+ private bool handle_message (SpiceProtocol.ForeignMenu.Msg* msg) {
+ switch (msg.id) {
+ case SpiceProtocol.ForeignMenu.MsgId.SET_TITLE:
+ var t = (SpiceProtocol.ForeignMenu.SetTitle*)(msg);
+ title = t.string;
+ break;
+ case SpiceProtocol.ForeignMenu.MsgId.ADD_ITEM:
+ var i = (SpiceProtocol.ForeignMenu.AddItem*)(msg);
+ debug ("add id:%u type:%u position:%u title:%s", i.id, i.type, i.position, i.string);
+ menu.items.append (new MenuItem ((int)i.id, i.string, get_menu_flags (i.type)));
+ notify_property ("menu");
+ break;
+ case SpiceProtocol.ForeignMenu.MsgId.MODIFY_ITEM:
+ debug ("deprecated: modify item");
+ break;
+ case SpiceProtocol.ForeignMenu.MsgId.REMOVE_ITEM:
+ var i = (SpiceProtocol.ForeignMenu.RmItem*)(msg);
+ debug ("not implemented: remove id:%u".printf (i.id));
+ break;
+ case SpiceProtocol.ForeignMenu.MsgId.CLEAR:
+ menu = new Menu ();
+ break;
+ default:
+ warn_if_reached ();
+ return false;
+ }
+ return true;
+ }
+
+ private async void handle_client (IOStream c) throws GLib.Error {
+ var header = SpiceProtocol.ForeignMenu.InitHeader ();
+ unowned uint8[] p = null;
+
+ debug ("new socket client, reading init header");
+
+ p = ((uint8[])(&header))[0:sizeof(SpiceProtocol.ForeignMenu.InitHeader)]; // FIXME vala
+ var read = yield c.input_stream.read_async (p);
+ if (warn_if (read != sizeof (SpiceProtocol.ForeignMenu.InitHeader)))
+ return;
+ if (warn_if (header.magic != SpiceProtocol.ForeignMenu.MAGIC))
+ return;
+ if (warn_if (header.version != SpiceProtocol.ForeignMenu.VERSION))
+ return;
+ if (warn_if (header.size < sizeof (SpiceProtocol.ForeignMenu.Init)))
+ return;
+
+ uint64 credentials = 0;
+ p = ((uint8[])(&credentials))[0:sizeof(uint64)];
+ read = yield c.input_stream.read_async (p);
+ if (warn_if (read != sizeof(uint64)))
+ return;
+ if (warn_if (credentials != 0))
+ return;
+
+ var title_size = header.size - sizeof(SpiceProtocol.ForeignMenu.Init);
+ var title = new uint8[title_size + 1];
+ read = yield c.input_stream.read_async (title[0:title_size]);
+ this.title = (string)title;
+
+ var t = new uint8[sizeof(SpiceProtocol.ForeignMenu.Msg)];
+ for (;;) {
+ read = yield c.input_stream.read_async (t[0:sizeof(SpiceProtocol.ForeignMenu.Msg)]);
+ if (read == 0)
+ break;
+
+ if (warn_if (read != sizeof (SpiceProtocol.ForeignMenu.Msg))) {
+ warning ("read only: " + read.to_string ());
+ break;
+ }
+
+ var msg = (SpiceProtocol.ForeignMenu.Msg*)t;
+ if (warn_if (msg.size < sizeof (SpiceProtocol.ForeignMenu.Msg)))
+ break;
+
+ if (msg.size > sizeof (SpiceProtocol.ForeignMenu.Msg)) {
+ t.resize ((int)msg.size);
+ msg = (SpiceProtocol.ForeignMenu.Msg*)t;
+ read = yield c.input_stream.read_async (t[sizeof(SpiceProtocol.ForeignMenu.Msg):msg.size]);
+ if (read == 0)
+ break;
+ if (warn_if (read != msg.size - sizeof(SpiceProtocol.ForeignMenu.Msg)))
+ break;
+ }
+
+ handle_message (msg);
+ }
+
+ }
+
+ public async void listen (string? addr = null) throws GLib.Error, SpiceCtrl.Error
+ {
+ var listener = Spice.ForeignMenuListener.new_listener (addr);
+
+ for (;;) {
+ var c = yield listener.accept_async ();
+ nclients += 1;
+ clients.append (c);
+ try {
+ yield handle_client (c);
+ } catch (GLib.Error e) {
+ warning (e.message);
+ }
+ c.close ();
+ clients.remove (c);
+ nclients -= 1;
+ }
+ }
+
+}
+
+} // SpiceCtrl