examples/edje: add multiseat example
authorBruno Dilly <bdilly@profusion.mobi>
Thu, 3 Nov 2016 21:10:51 +0000 (19:10 -0200)
committerBruno Dilly <bdilly@profusion.mobi>
Mon, 19 Dec 2016 16:58:35 +0000 (14:58 -0200)
src/examples/edje/.gitignore
src/examples/edje/Makefile.am
src/examples/edje/edje-multiseat.c [new file with mode: 0644]
src/examples/edje/multiseat.edc [new file with mode: 0644]

index 5b5f5f0..a10bdf9 100644 (file)
@@ -11,6 +11,7 @@
 /edje-drag
 /edje-edit-part-box
 /edje-entry
+/edje-multiseat
 /edje-multisense
 /edje-perspective
 /edje-signals-messages
index 7dff1e3..6198ccf 100644 (file)
@@ -43,6 +43,7 @@ external_elm_panes.edc \
 external_emotion_elm.edc \
 lua_script.edc \
 messages_echo.edc \
+multiseat.edc \
 perspective.edc \
 signals-messages.edc \
 signalsBubble.edc \
@@ -140,6 +141,7 @@ edje-color-class.c \
 edje-drag.c \
 edje-edit-part-box.c \
 edje-entry.c \
+edje-multiseat.c \
 edje-multisense.c \
 edje-perspective.c \
 edje-signals-messages.c \
@@ -213,6 +215,7 @@ edje-color-class \
 edje-drag\
 edje-edit-part-box \
 edje-entry \
+edje-multiseat \
 edje-perspective \
 edje-signals-messages \
 edje-swallow \
diff --git a/src/examples/edje/edje-multiseat.c b/src/examples/edje/edje-multiseat.c
new file mode 100644 (file)
index 0000000..4fbc77c
--- /dev/null
@@ -0,0 +1,211 @@
+/**
+ * Edje example demonstrating how to use multiseat features.
+ *
+ * You'll need at least one Evas engine built for it (excluding the
+ * buffer one) that supports multiseat. It may be wayland or
+ * X11 with VNC support. Using other engines will lead you to a
+ * situation where all seats are reported as the same one ("default").
+ *
+ * @verbatim
+ * edje_cc multiseat.edc && gcc -o edje-multiseat edje-multiseat.c `pkg-config --libs --cflags evas ecore ecore-evas edje`
+ * @endverbatim
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#else
+# define EINA_UNUSED
+#endif
+
+#ifndef PACKAGE_DATA_DIR
+#define PACKAGE_DATA_DIR "."
+#endif
+
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include <Edje.h>
+
+#define WIDTH  400
+#define HEIGHT 400
+
+static const char *GROUPNAME = "example/main";
+static const char *PARTNAME_KNOB1 = "example/knob1";
+static const char *PARTNAME_KNOB2 = "example/knob2";
+
+static void
+_on_destroy(Ecore_Evas *ee EINA_UNUSED)
+{
+   ecore_main_loop_quit();
+}
+
+static void
+_on_canvas_resize(Ecore_Evas *ee)
+{
+   Evas_Object *bg;
+   Evas_Object *edje_obj;
+   int w, h;
+
+   bg = ecore_evas_data_get(ee, "background");
+   edje_obj = ecore_evas_data_get(ee, "edje_obj");
+
+   ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
+   evas_object_resize(bg, w, h);
+   evas_object_resize(edje_obj, w, h);
+}
+
+static void
+_on_key_down(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *o, void *event_info)
+{
+   Evas_Event_Key_Down *ev = event_info;
+   Efl_Input_Device *seat;
+
+   seat = efl_input_device_seat_get(ev->dev);
+   printf("Seat %s (%s) pressed key %s\n", efl_input_device_name_get(seat),
+           edje_obj_seat_name_get(o, seat), ev->key);
+}
+
+static void
+_on_drag_started(void *data EINA_UNUSED, Evas_Object *o, const char *emission, const char *source)
+{
+   Eina_Stringshare *seat_name;
+   const char *seat_name_str;
+   Efl_Input_Device *seat;
+
+   seat_name_str = emission + strlen("drag,start,");
+   seat_name = eina_stringshare_add(seat_name_str);
+   seat = edje_obj_seat_get(o, seat_name);
+   printf("Seat %s (%s) started drag %s\n", efl_input_device_name_get(seat),
+           seat_name, source);
+   eina_stringshare_del(seat_name);
+}
+
+static void
+_setup_drag(Evas_Object *edje_obj, const char *partname)
+{
+   if (!edje_object_part_drag_size_set(edje_obj, partname, 1.0, 0.4))
+     printf("error when setting drag size.\n");
+
+   if (!edje_object_part_drag_step_set(edje_obj, partname, 0.0, 0.1))
+     printf("error when setting drag step size.\n");
+
+   if (!edje_object_part_drag_page_set(edje_obj, partname, 0.0, 0.3))
+     printf("error when setting drag page step size.\n");
+
+   edje_object_signal_callback_add(edje_obj, "drag,start,*", partname,
+                                   _on_drag_started, NULL);
+}
+
+static void
+_edje_load_cb(void *data EINA_UNUSED, Evas_Object *o EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED)
+{
+   printf("Edje loaded\n");
+}
+
+static void
+_edje_seat_cb(void *data EINA_UNUSED, Evas_Object *o EINA_UNUSED, const char *emission, const char *source EINA_UNUSED)
+{
+   const char *sig;
+
+   sig = emission + strlen("seat,");
+   printf("Device %s\n", sig);
+}
+
+static void
+_device_added(void *data, const Efl_Event *event)
+{
+   Efl_Input_Device *dev = event->info;
+   Evas_Object *edje_obj = data;
+
+   if (efl_input_device_type_get(dev) != EFL_INPUT_DEVICE_CLASS_SEAT)
+     return;
+
+   efl_canvas_object_seat_focus_add(edje_obj, dev);
+}
+
+int
+main(int argc EINA_UNUSED, char *argv[] EINA_UNUSED)
+{
+   const char *edje_file = PACKAGE_DATA_DIR"/multiseat.edj";
+   const Eina_List *devices, *l;
+   Evas_Object *edje_obj, *bg;
+   Efl_Input_Device *dev;
+   Ecore_Evas *ee;
+   Evas *evas;
+
+   if (!ecore_evas_init())
+     return EXIT_FAILURE;
+
+   if (!edje_init())
+     goto shutdown_ecore_evas;
+
+   ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL);
+   if (!ee) goto shutdown_edje;
+
+   ecore_evas_callback_destroy_set(ee, _on_destroy);
+   ecore_evas_callback_resize_set(ee, _on_canvas_resize);
+   ecore_evas_title_set(ee, "Edje Multiseat Example");
+
+   evas = ecore_evas_get(ee);
+
+   bg = evas_object_rectangle_add(evas);
+   evas_object_color_set(bg, 255, 255, 255, 255);
+   evas_object_resize(bg, WIDTH, HEIGHT);
+   evas_object_show(bg);
+   ecore_evas_data_set(ee, "background", bg);
+
+   edje_obj = edje_object_add(evas);
+
+   if (!edje_object_file_set(edje_obj, edje_file, GROUPNAME))
+     printf("failed to set file %s.\n", edje_file);
+
+   evas_object_move(edje_obj, 0, 0);
+   evas_object_resize(edje_obj, WIDTH, HEIGHT);
+   evas_object_show(edje_obj);
+   ecore_evas_data_set(ee, "edje_obj", edje_obj);
+
+   _setup_drag(edje_obj, PARTNAME_KNOB1);
+   _setup_drag(edje_obj, PARTNAME_KNOB2);
+
+   edje_object_part_text_set(edje_obj, "example/text1", "Type here : ");
+   edje_object_part_text_cursor_end_set(edje_obj, "example/text1",
+                                        EDJE_CURSOR_MAIN);
+
+   edje_object_part_text_set(edje_obj, "example/text2", "Or maybe here : ");
+   edje_object_part_text_cursor_end_set(edje_obj, "example/text2",
+                                        EDJE_CURSOR_MAIN);
+
+   devices = evas_device_list(evas, NULL);
+   EINA_LIST_FOREACH(devices, l, dev)
+     {
+        if (efl_input_device_type_get(dev) == EFL_INPUT_DEVICE_CLASS_SEAT)
+          efl_canvas_object_seat_focus_add(edje_obj, dev);
+
+     }
+   efl_event_callback_add(evas, EFL_CANVAS_EVENT_DEVICE_ADDED,
+                          _device_added, edje_obj);
+   evas_object_event_callback_add(edje_obj, EVAS_CALLBACK_KEY_DOWN,
+                                  _on_key_down, NULL);
+   edje_object_signal_callback_add(edje_obj, "seat,*", "",
+                                   _edje_seat_cb, NULL);
+   edje_object_signal_callback_add(edje_obj, "load", "",
+                                   _edje_load_cb, NULL);
+
+   printf("Running example on evas engine %s\n",
+          ecore_evas_engine_name_get(ee));
+   ecore_evas_show(ee);
+
+   ecore_main_loop_begin();
+
+   ecore_evas_free(ee);
+   ecore_evas_shutdown();
+   edje_shutdown();
+
+   return EXIT_SUCCESS;
+
+shutdown_edje:
+   edje_shutdown();
+shutdown_ecore_evas:
+   ecore_evas_shutdown();
+
+   return EXIT_FAILURE;
+}
diff --git a/src/examples/edje/multiseat.edc b/src/examples/edje/multiseat.edc
new file mode 100644 (file)
index 0000000..aa16309
--- /dev/null
@@ -0,0 +1,740 @@
+collections {
+
+   styles {
+      style {
+         name: "entry_style";
+         base: "font="sans" font_size=10 color=#000 wrap="word" left_margin=2 right_margin=2";
+      }
+   }
+
+   group {
+      name: "example/main";
+      min: 400 400;
+
+      parts {
+         part {
+            name: "bg";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+            }
+         }
+
+         part {
+            name: "title";
+            type: TEXT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               color: 0 0 0 255;
+               rel1 {
+                  relative: 0.0 0.0;
+                  offset: 0 0;
+                  to: "bg";
+               }
+               rel2 {
+                  relative: 1.0 0.2;
+                  offset: -1 -1;
+                  to: "bg";
+               }
+               text {
+                  text: "Multiseat Example";
+                  size: 16;
+                  font: "sans";
+                  min: 1 1;
+               }
+            }
+         }
+
+         part {
+            name: "drag_area1";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               color: 0 0 0 255;
+               rel1 {
+                  relative: 0.1 0.25;
+                  offset: -5 0;
+               }
+               rel2 {
+                  relative: 0.1 0.9;
+                  offset: 4 0;
+               }
+            }
+         }
+
+         part {
+            name: "example/knob1";
+            type: RECT;
+            mouse_events: 1;
+            dragable {
+               confine: "drag_area1";
+               x: 0 0 0;
+               y: 1 1 0;
+            }
+            description {
+               state: "default" 0.0;
+               min: 10 10;
+               color: 200 200 200 255;
+            }
+            description {
+               state: "focused,s1" 0.0;
+               inherit: "default" 0.0;
+               color: 200 0 0 255;
+            }
+            description {
+               state: "focused,s2" 0.0;
+               inherit: "default" 0.0;
+               color: 0 200 0 255;
+            }
+         }
+
+         part {
+            name: "drag_area2";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               color: 0 0 0 255;
+               rel1 {
+                  relative: 0.2 0.25;
+                  offset: -5 0;
+               }
+               rel2 {
+                  relative: 0.2 0.9;
+                  offset: 4 0;
+               }
+            }
+         }
+
+         part {
+            name: "example/knob2";
+            type: RECT;
+            mouse_events: 1;
+            dragable {
+               confine: "drag_area2";
+               x: 0 0 0;
+               y: 1 1 0;
+            }
+            description {
+               state: "default" 0.0;
+               min: 10 10;
+               color: 200 200 200 255;
+            }
+            description {
+               state: "focused,s1" 0.0;
+               inherit: "default" 0.0;
+               color: 200 0 0 255;
+            }
+            description {
+               state: "focused,s2" 0.0;
+               inherit: "default" 0.0;
+               color: 0 200 0 255;
+            }
+         }
+
+         part {
+            name: "bg_text1";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               min: 10 50;
+               rel1.relative: 0.3 0.25;
+               rel2.relative: 0.9 0.4;
+               color: 200 200 200 255;
+            }
+            description {
+               state: "focused,s1" 0.0;
+               inherit: "default" 0.0;
+               color: 200 0 0 255;
+            }
+            description {
+               state: "focused,s2" 0.0;
+               inherit: "default" 0.0;
+               color: 0 200 0 255;
+            }
+         }
+
+         part {
+            name: "example/text1";
+            type: TEXTBLOCK;
+            scale: 1;
+            entry_mode: EDITABLE;
+            select_mode: DEFAULT;
+            cursor_mode: UNDER;
+            mouse_events: 1;
+            multiline: 1;
+            source: "example/selection";
+            source4: "example/cursor";
+            description {
+               state: "default" 0.0;
+               min: 12 50;
+               rel1 {
+                  to: "bg_text1";
+                  offset: 2 2;
+               }
+               rel2 {
+                  to: "bg_text1";
+                  offset: -3 -3;
+               }
+               text {
+                  style: "entry_style";
+                  min: 0 1;
+                  align: 0.0 0.0;
+               }
+            }
+         }
+
+         part {
+            name: "bg_text2";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               min: 10 50;
+               rel1.relative: 0.3 0.45;
+               rel2.relative: 0.9 0.6;
+               color: 200 200 200 255;
+            }
+            description {
+               state: "focused,s1" 0.0;
+               inherit: "default" 0.0;
+               color: 200 0 0 255;
+            }
+            description {
+               state: "focused,s2" 0.0;
+               inherit: "default" 0.0;
+               color: 0 200 0 255;
+            }
+         }
+
+         part {
+            name: "example/text2";
+            type: TEXTBLOCK;
+            scale: 1;
+            entry_mode: EDITABLE;
+            select_mode: DEFAULT;
+            cursor_mode: UNDER;
+            mouse_events: 1;
+            multiline: 1;
+            source: "example/selection";
+            source4: "example/cursor";
+            description {
+               state: "default" 0.0;
+               min: 12 50;
+               rel1 {
+                  to: "bg_text2";
+                  offset: 2 2;
+               }
+               rel2 {
+                  to: "bg_text2";
+                  offset: -3 -3;
+               }
+               text {
+                  style: "entry_style";
+                  min: 0 1;
+                  align: 0.0 0.0;
+               }
+            }
+         }
+
+         part {
+            name: "button_bg";
+            type: RECT;
+            mouse_events: 1;
+            description {
+               state: "default" 0.0;
+               rel1.relative: 0.3 0.65;
+               rel2.relative: 0.9 0.9;
+               color: 200 200 200 255;
+            }
+         }
+
+         part {
+            name: "button_area";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               rel1 {
+                  to: "button_bg";
+                  offset: 10 10;
+               }
+               rel2 {
+                  to: "button_bg";
+                  offset: -11 -11;
+               }
+               color: 0 0 0 0;
+            }
+         }
+
+         part {
+            name: "button_left_over";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               rel1 {
+                  to: "button_area";
+                  relative: 0.0 0.0;
+               }
+               rel2 {
+                  to: "button_area";
+                  relative: 0.5 0.5;
+               }
+               color: 255 0 0 100;
+            }
+            description {
+               state: "on" 0.0;
+               inherit: "default" 0.0;
+               color: 255 0 0 255;
+            }
+         }
+
+         part {
+            name: "button_left_over_label";
+            type: TEXT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               color: 0 0 0 255;
+               rel1.to: "button_left_over";
+               rel2.to: "button_left_over";
+               text {
+                  text: "Seat 1 over";
+                  size: 12;
+                  font: "sans";
+                  min: 1 1;
+               }
+            }
+         }
+
+         part {
+            name: "button_left_focus";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               rel1 {
+                  to: "button_area";
+                  relative: 0.0 0.5;
+               }
+               rel2 {
+                  to: "button_area";
+                  relative: 0.5 1.0;
+               }
+               color: 255 0 0 100;
+            }
+            description {
+               state: "on" 0.0;
+               inherit: "default" 0.0;
+               color: 255 0 0 255;
+            }
+         }
+
+         part {
+            name: "button_left_focus_label";
+            type: TEXT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               color: 0 0 0 255;
+               rel1.to: "button_left_focus";
+               rel2.to: "button_left_focus";
+               text {
+                  text: "Seat 1 focus";
+                  size: 12;
+                  font: "sans";
+                  min: 1 1;
+               }
+            }
+         }
+
+         part {
+            name: "button_right_over";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               rel1 {
+                  to: "button_area";
+                  relative: 0.5 0.0;
+               }
+               rel2 {
+                  to: "button_area";
+                  relative: 1.0 0.5;
+               }
+               color: 0 255 0 100;
+            }
+            description {
+               state: "on" 0.0;
+               inherit: "default" 0.0;
+               color: 0 255 0 255;
+            }
+         }
+
+         part {
+            name: "button_right_over_label";
+            type: TEXT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               color: 0 0 0 255;
+               rel1.to: "button_right_over";
+               rel2.to: "button_right_over";
+               text {
+                  text: "Seat 2 over";
+                  size: 12;
+                  font: "sans";
+                  min: 1 1;
+               }
+            }
+         }
+
+         part {
+            name: "button_right_focus";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               rel1 {
+                  to: "button_area";
+                  relative: 0.5 0.5;
+               }
+               rel2 {
+                  to: "button_area";
+                  relative: 1.0 1.0;
+               }
+               color: 0 255 0 100;
+            }
+            description {
+               state: "on" 0.0;
+               inherit: "default" 0.0;
+               color: 0 255 0 255;
+            }
+         }
+
+         part {
+            name: "button_right_focus_label";
+            type: TEXT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               color: 0 0 0 255;
+               rel1.to: "button_right_focus";
+               rel2.to: "button_right_focus";
+               text {
+                  text: "Seat 2 focus";
+                  size: 12;
+                  font: "sans";
+                  min: 1 1;
+               }
+            }
+         }
+      }
+
+      programs {
+         program {
+            name: "focus";
+            signal: "load";
+            source: "";
+            action: FOCUS_SET "seat1";
+            target: "example/text1";
+         }
+
+         program {
+            name: "focus,t1,s1";
+            signal: "mouse,clicked,1,seat1";
+            source: "example/text1";
+            action: FOCUS_SET "seat1";
+            target: "example/text1";
+         }
+
+         program {
+            name: "focus,color,t1,s1";
+            signal: "focus,part,in,seat1";
+            source: "example/text1";
+            action: STATE_SET "focused,s1" 0.0;
+            target: "bg_text1";
+         }
+
+         program {
+            name: "unfocus,color,t1,s1";
+            signal: "focus,part,out,seat1";
+            source: "example/text1";
+            action: STATE_SET "default" 0.0;
+            target: "bg_text1";
+         }
+
+         program {
+            name: "focus,t2,s1";
+            signal: "mouse,clicked,1,seat1";
+            source: "example/text2";
+            action: FOCUS_SET "seat1";
+            target: "example/text2";
+         }
+
+         program {
+            name: "focus,color,t2,s1";
+            signal: "focus,part,in,seat1";
+            source: "example/text2";
+            action: STATE_SET "focused,s1" 0.0;
+            target: "bg_text2";
+         }
+
+         program {
+            name: "unfocus,color,t2,s1";
+            signal: "focus,part,out,seat1";
+            source: "example/text2";
+            action: STATE_SET "default" 0.0;
+            target: "bg_text2";
+         }
+
+         program {
+            name: "focus,t1,s2";
+            signal: "mouse,clicked,1,seat2";
+            source: "example/text1";
+            action: FOCUS_SET "seat2";
+            target: "example/text1";
+         }
+
+         program {
+            name: "focus,color,t1,s2";
+            signal: "focus,part,in,seat2";
+            source: "example/text1";
+            action: STATE_SET "focused,s2" 0.0;
+            target: "bg_text1";
+         }
+
+         program {
+            name: "unfocus,color,t1,s2";
+            signal: "focus,part,out,seat2";
+            source: "example/text1";
+            action: STATE_SET "default" 0.0;
+            target: "bg_text1";
+         }
+
+         program {
+            name: "focus,t2,s2";
+            signal: "mouse,clicked,1,seat2";
+            source: "example/text2";
+            action: FOCUS_SET "seat2";
+            target: "example/text2";
+         }
+
+         program {
+            name: "focus,color,t2,s2";
+            signal: "focus,part,in,seat2";
+            source: "example/text2";
+            action: STATE_SET "focused,s2" 0.0;
+            target: "bg_text2";
+         }
+
+         program {
+            name: "unfocus,color,t2,s2";
+            signal: "focus,part,out,seat2";
+            source: "example/text2";
+            action: STATE_SET "default" 0.0;
+            target: "bg_text2";
+         }
+
+         program {
+            name: "button,left,in";
+            signal: "mouse,in,seat1";
+            source: "button_bg";
+            action: STATE_SET "on" 0.0;
+            target: "button_left_over";
+         }
+
+         program {
+            name: "button,left,out";
+            signal: "mouse,out,seat1";
+            source: "button_bg";
+            action: STATE_SET "default" 0.0;
+            target: "button_left_over";
+         }
+
+         program {
+            name: "button,left,focus";
+            signal: "mouse,clicked,1,seat1";
+            source: "button_bg";
+            action: FOCUS_SET "seat1";
+            target: "button_bg";
+         }
+
+         program {
+            name: "button,left,focused";
+            signal: "focus,part,in,seat1";
+            source: "button_bg";
+            action: STATE_SET "on" 0.0;
+            target: "button_left_focus";
+         }
+
+         program {
+            name: "button,left,unfocused";
+            signal: "focus,part,out,seat1";
+            source: "button_bg";
+            action: STATE_SET "default" 0.0;
+            target: "button_left_focus";
+         }
+
+         program {
+            name: "button,right,in";
+            signal: "mouse,in,seat2";
+            source: "button_bg";
+            action: STATE_SET "on" 0.0;
+            target: "button_right_over";
+         }
+
+         program {
+            name: "button,right,out";
+            signal: "mouse,out,seat2";
+            source: "button_bg";
+            action: STATE_SET "default" 0.0;
+            target: "button_right_over";
+         }
+
+         program {
+            name: "button,right,focus";
+            signal: "mouse,clicked,1,seat2";
+            source: "button_bg";
+            action: FOCUS_SET "seat2";
+            target: "button_bg";
+         }
+
+         program {
+            name: "button,right,focused";
+            signal: "focus,part,in,seat2";
+            source: "button_bg";
+            action: STATE_SET "on" 0.0;
+            target: "button_right_focus";
+         }
+
+         program {
+            name: "button,right,unfocused";
+            signal: "focus,part,out,seat2";
+            source: "button_bg";
+            action: STATE_SET "default" 0.0;
+            target: "button_right_focus";
+         }
+
+         program {
+            name: "knob1,s1";
+            signal: "drag,start,seat1";
+            source: "example/knob1";
+            action: STATE_SET "focused,s1" 0.0;
+            target: "example/knob1";
+         }
+
+         program {
+            name: "knob1,s2";
+            signal: "drag,start,seat2";
+            source: "example/knob1";
+            action: STATE_SET "focused,s2" 0.0;
+            target: "example/knob1";
+         }
+
+         program {
+            name: "knob1,seat1";
+            signal: "drag,stop,*";
+            source: "example/knob1";
+            action: STATE_SET "default" 0.0;
+            target: "example/knob1";
+         }
+
+         program {
+            name: "knob2,s1";
+            signal: "drag,start,seat1";
+            source: "example/knob2";
+            action: STATE_SET "focused,s1" 0.0;
+            target: "example/knob2";
+         }
+
+         program {
+            name: "knob2,s2";
+            signal: "drag,start,seat2";
+            source: "example/knob2";
+            action: STATE_SET "focused,s2" 0.0;
+            target: "example/knob2";
+         }
+
+         program {
+            name: "knob2,seat1";
+            signal: "drag,stop,*";
+            source: "example/knob2";
+            action: STATE_SET "default" 0.0;
+            target: "example/knob2";
+         }
+      }
+   }
+
+   group {
+      name: "example/selection";
+
+      parts {
+         part {
+            name: "selection";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               color: 180 180 180 255;
+            }
+         }
+      }
+   }
+
+   group {
+      name: "example/cursor";
+      min: 1 0;
+
+      parts {
+         part {
+            name: "cursor";
+            type: RECT;
+            mouse_events: 0;
+            description {
+               state: "default" 0.0;
+               min: 2 12;
+               color: 0 0 0 255;
+            }
+            description {
+               state: "hidden" 0.0;
+               inherit: "default" 0.0;
+               color: 0 0 0 0;
+            }
+         }
+      }
+
+      programs {
+         program {
+            name: "cursor_hide";
+            signal: "load";
+            source: "";
+            action: STATE_SET "hidden" 0.0;
+            target: "cursor";
+            transition: SINUSOIDAL 0.2;
+            after: "cursor_hide_timer";
+         }
+         program {
+            name: "cursor_hide_timer";
+            in: 0.2 0.0;
+            after: "cursor_show";
+         }
+         program {
+            name: "cursor_show";
+            action: STATE_SET "default" 0.0;
+            target: "cursor";
+            after: "cursor_show_timer";
+         }
+         program {
+            name: "cursor_show_timer";
+            in: 0.5 0.0;
+            after: "cursor_hide";
+         }
+      }
+   }
+}