diff --git a/.gitignore b/.gitignore
index acf5dd9..ab0c578 100644
--- a/.gitignore
+++ b/.gitignore
@@ -85,4 +85,5 @@ dkms.conf
*.exe
*.out
*.app
-
+build
+.vscode
\ No newline at end of file
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..60433b1
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,59 @@
+project('diya-shell',
+ ['c'],
+ version: '0.1.0',
+ license: 'MIT',
+ meson_version: '>=0.51.0',
+ default_options: ['c_std=gnu11', 'warning_level=3'])
+
+lib_so_version = '0'
+
+add_project_arguments(
+ [
+ '-Wno-pedantic',
+ '-Werror=implicit-function-declaration',
+ '-Werror=return-type',
+ ],
+ language: 'c')
+
+gtk = dependency('gtk4')
+wayland_client = dependency('wayland-client', version: '>=1.10.0')
+
+# wayland_scanner is required, but we can find it without pkg-config
+wayland_scanner = find_program('wayland-scanner')
+
+# use system xdg-shell protocol when available
+#wayland_protocols = dependency('wayland-protocols', version: '>=1.16')
+
+# pkg_config = import('pkgconfig')
+# gnome = import('gnome')
+
+gtk_layer_shell = dependency('gtk4-layer-shell-0', version: '>=1.0.2')
+wayland_targets=[]
+
+wayland_protos = [
+ 'protocols/wlr-foreign-toplevel-management-unstable-v1'
+]
+
+foreach proto : wayland_protos
+ xml = ''.join([proto,'.xml'])
+ header = ''.join([proto.split('/').get(-1),'.h'])
+ cfile = ''.join([proto.split('/').get(-1),'.c'])
+ wayland_targets += custom_target(header,output:header,input:xml,
+ command: [ wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@' ] )
+ wayland_targets += custom_target(cfile,output:cfile,input:xml,
+ command: [ wayland_scanner, 'public-code', '@INPUT@', '@OUTPUT@' ] )
+endforeach
+
+src = [
+ 'src/base.c',
+ 'src/launcher.c',
+ 'src/background.c',
+ 'src/wayland.c',
+ 'src/shell.c',
+ 'src/foreign.c',
+ wayland_targets]
+
+executable(
+ 'diya-shell',
+ src,
+ dependencies: [gtk, gtk_layer_shell, wayland_client])
\ No newline at end of file
diff --git a/protocols/wlr-foreign-toplevel-management-unstable-v1.xml b/protocols/wlr-foreign-toplevel-management-unstable-v1.xml
new file mode 100644
index 0000000..1081337
--- /dev/null
+++ b/protocols/wlr-foreign-toplevel-management-unstable-v1.xml
@@ -0,0 +1,270 @@
+
+
+
+ Copyright © 2018 Ilia Bozhinov
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+
+
+
+
+ The purpose of this protocol is to enable the creation of taskbars
+ and docks by providing them with a list of opened applications and
+ letting them request certain actions on them, like maximizing, etc.
+
+ After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
+ toplevel window will be sent via the toplevel event
+
+
+
+
+ This event is emitted whenever a new toplevel window is created. It
+ is emitted for all toplevels, regardless of the app that has created
+ them.
+
+ All initial details of the toplevel(title, app_id, states, etc.) will
+ be sent immediately after this event via the corresponding events in
+ zwlr_foreign_toplevel_handle_v1.
+
+
+
+
+
+
+ Indicates the client no longer wishes to receive events for new toplevels.
+ However the compositor may emit further toplevel_created events, until
+ the finished event is emitted.
+
+ The client must not send any more requests after this one.
+
+
+
+
+
+ This event indicates that the compositor is done sending events to the
+ zwlr_foreign_toplevel_manager_v1. The server will destroy the object
+ immediately after sending this request, so it will become invalid and
+ the client should free any resources associated with it.
+
+
+
+
+
+
+ A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
+ window. Each app may have multiple opened toplevels.
+
+ Each toplevel has a list of outputs it is visible on, conveyed to the
+ client with the output_enter and output_leave events.
+
+
+
+
+ This event is emitted whenever the title of the toplevel changes.
+
+
+
+
+
+
+ This event is emitted whenever the app-id of the toplevel changes.
+
+
+
+
+
+
+ This event is emitted whenever the toplevel becomes visible on
+ the given output. A toplevel may be visible on multiple outputs.
+
+
+
+
+
+
+ This event is emitted whenever the toplevel stops being visible on
+ the given output. It is guaranteed that an entered-output event
+ with the same output has been emitted before this event.
+
+
+
+
+
+
+ Requests that the toplevel be maximized. If the maximized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Requests that the toplevel be unmaximized. If the maximized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Requests that the toplevel be minimized. If the minimized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Requests that the toplevel be unminimized. If the minimized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Request that this toplevel be activated on the given seat.
+ There is no guarantee the toplevel will be actually activated.
+
+
+
+
+
+
+ The different states that a toplevel can have. These have the same meaning
+ as the states with the same names defined in xdg-toplevel
+
+
+
+
+
+
+
+
+
+
+ This event is emitted immediately after the zlw_foreign_toplevel_handle_v1
+ is created and each time the toplevel state changes, either because of a
+ compositor action or because of a request in this protocol.
+
+
+
+
+
+
+
+ This event is sent after all changes in the toplevel state have been
+ sent.
+
+ This allows changes to the zwlr_foreign_toplevel_handle_v1 properties
+ to be seen as atomic, even if they happen via multiple events.
+
+
+
+
+
+ Send a request to the toplevel to close itself. The compositor would
+ typically use a shell-specific method to carry out this request, for
+ example by sending the xdg_toplevel.close event. However, this gives
+ no guarantees the toplevel will actually be destroyed. If and when
+ this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
+ be emitted.
+
+
+
+
+
+ The rectangle of the surface specified in this request corresponds to
+ the place where the app using this protocol represents the given toplevel.
+ It can be used by the compositor as a hint for some operations, e.g
+ minimizing. The client is however not required to set this, in which
+ case the compositor is free to decide some default value.
+
+ If the client specifies more than one rectangle, only the last one is
+ considered.
+
+ The dimensions are given in surface-local coordinates.
+ Setting width=height=0 removes the already-set rectangle.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This event means the toplevel has been destroyed. It is guaranteed there
+ won't be any more events for this zwlr_foreign_toplevel_handle_v1. The
+ toplevel itself becomes inert so any requests will be ignored except the
+ destroy request.
+
+
+
+
+
+ Destroys the zwlr_foreign_toplevel_handle_v1 object.
+
+ This request should be called either when the client does not want to
+ use the toplevel anymore or after the closed event to finalize the
+ destruction of the object.
+
+
+
+
+
+
+
+ Requests that the toplevel be fullscreened on the given output. If the
+ fullscreen state and/or the outputs the toplevel is visible on actually
+ change, this will be indicated by the state and output_enter/leave
+ events.
+
+ The output parameter is only a hint to the compositor. Also, if output
+ is NULL, the compositor should decide which output the toplevel will be
+ fullscreened on, if at all.
+
+
+
+
+
+
+ Requests that the toplevel be unfullscreened. If the fullscreen state
+ actually changes, this will be indicated by the state event.
+
+
+
+
+
+
+
+ This event is emitted whenever the parent of the toplevel changes.
+
+ No event is emitted when the parent handle is destroyed by the client.
+
+
+
+
+
diff --git a/src/background.c b/src/background.c
new file mode 100644
index 0000000..ef87bb9
--- /dev/null
+++ b/src/background.c
@@ -0,0 +1,33 @@
+#include "background.h"
+#include
+#define NAMESPACE "background"
+
+static void on_background_destroy(GtkWindow *window, GApplication *_data)
+{
+ (void) window;
+ (void)_data;
+ //g_application_quit (G_APPLICATION (gtk_window_get_application (window)));
+}
+
+void diya_shell_init_background(DiyaShell * shell)
+{
+ GtkWindow *gtk_window;
+ g_object_get(shell, "background", >k_window, NULL);
+ assert(gtk_window);
+ g_signal_connect (gtk_window, "destroy", G_CALLBACK (on_background_destroy), NULL);
+ // int layer shell for window
+ gtk_layer_init_for_window (gtk_window);
+ // anchor window to all edges
+ for (int i = 0; i < GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER; i++)
+ gtk_layer_set_anchor (gtk_window, i, true);
+ // set margin on window
+ for (int i = 0; i < GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER; i++)
+ gtk_layer_set_margin (gtk_window, i, 0);
+ gtk_layer_set_layer (gtk_window, GTK_LAYER_SHELL_LAYER_BACKGROUND);
+ gtk_layer_set_keyboard_mode (gtk_window, GTK_LAYER_SHELL_KEYBOARD_MODE_NONE);
+ gtk_layer_set_namespace (gtk_window, NAMESPACE);
+ gtk_widget_set_name(GTK_WIDGET(gtk_window),NAMESPACE);
+ //gtk_layer_auto_exclusive_zone_enable (gtk_window);
+ //g_signal_connect (gtk_window, "orientation-changed", G_CALLBACK (on_orientation_changed), /*data*/NULL);
+ gtk_window_present (GTK_WINDOW (gtk_window));
+}
\ No newline at end of file
diff --git a/src/background.h b/src/background.h
new file mode 100644
index 0000000..f3e5fe3
--- /dev/null
+++ b/src/background.h
@@ -0,0 +1,8 @@
+#ifndef DIYA_SHELL_BACKGROUND_H
+#define DIYA_SHELL_BACKGROUND_H
+
+#include "shell.h"
+
+void diya_shell_init_background(DiyaShell * shell);
+
+#endif
\ No newline at end of file
diff --git a/src/base.c b/src/base.c
new file mode 100644
index 0000000..546e690
--- /dev/null
+++ b/src/base.c
@@ -0,0 +1,21 @@
+#include "base.h"
+
+G_DEFINE_ABSTRACT_TYPE(DiyaObject, diya_object, G_TYPE_OBJECT)
+
+static void diya_object_class_init(DiyaObjectClass *class)
+{
+ class->to_string = NULL;
+}
+
+static void diya_object_init(DiyaObject *self)
+{
+ (void) self;
+}
+
+const gchar * diya_object_to_string(gpointer object)
+{
+ g_return_val_if_fail(DIYA_IS_OBJECT(object), NULL);
+ DiyaObject * self = DIYA_OBJECT(object);
+ DiyaObjectClass *class = DIYA_OBJECT_GET_CLASS(self);
+ return class->to_string ? class->to_string(self) : NULL;
+}
\ No newline at end of file
diff --git a/src/base.h b/src/base.h
new file mode 100644
index 0000000..7efcc69
--- /dev/null
+++ b/src/base.h
@@ -0,0 +1,24 @@
+#ifndef DIYA_OBJECT_H
+#define DIYA_OBJECT_H
+
+#include
+/**
+ * Base class object
+ *
+ */
+#define DIYA_TYPE_OBJECT (diya_object_get_type ())
+G_DECLARE_DERIVABLE_TYPE (DiyaObject, diya_object, DIYA, OBJECT, GObject)
+
+struct _DiyaObjectClass {
+ GObjectClass parent_class;
+ const gchar* (*to_string) (DiyaObject *self);
+ /**
+ * @brief reserve for futur use to
+ * define common API for descendants
+ *
+ */
+};
+
+const gchar* diya_object_to_string(gpointer object);
+
+#endif
\ No newline at end of file
diff --git a/src/foreign.c b/src/foreign.c
new file mode 100644
index 0000000..f7050b4
--- /dev/null
+++ b/src/foreign.c
@@ -0,0 +1,313 @@
+#include
+#include
+#include "foreign.h"
+
+/**
+ * @DiyaWindow Object definition
+ *
+ */
+enum
+{
+ NO_PROP,
+ WIN_APP_ID,
+ WIN_TITLE,
+ WIN_HANDLE,
+ WIN_STATE,
+ WIN_PARENT,
+ SHELL,
+ N_PROPERTIES
+};
+static GParamSpec *win_properties[N_PROPERTIES] = {0};
+struct _DiyaWindow
+{
+ DiyaObject parent;
+ gchar * appid;
+ gpointer handle;
+ gchar* title;
+ enum diya_win_state state;
+ DiyaWindow * parent_win;
+ DiyaShell * shell;
+ gchar string[128];
+};
+G_DEFINE_TYPE(DiyaWindow, diya_window, DIYA_TYPE_OBJECT)
+
+static void diya_window_finalize(GObject* object)
+{
+ DiyaWindow * self = DIYA_WINDOW(object);
+ g_debug("diya_window_finalize: %s", diya_object_to_string(self));
+ if(self->appid)
+ {
+ g_free(self->appid);
+ }
+ if(self->title)
+ {
+ g_free(self->title);
+ }
+}
+
+static void diya_window_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ DiyaWindow * self = DIYA_WINDOW(object);
+ switch (property_id)
+ {
+ case WIN_APP_ID:
+ if(self->appid)
+ {
+ g_free(self->appid);
+ }
+ self->appid = g_strdup(g_value_get_string(value));
+ break;
+ case WIN_TITLE:
+ if(self->title)
+ {
+ g_free(self->title);
+ }
+ self->title = g_strdup(g_value_get_string(value));
+ break;
+
+ case WIN_HANDLE:
+ self->handle = g_value_get_pointer(value);
+ break;
+ case WIN_PARENT:
+ self->parent_win = g_value_get_pointer(value);
+ break;
+ case SHELL:
+ self->shell = g_value_get_pointer(value);
+ break;
+ case WIN_STATE:
+ self->state = g_value_get_uint(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void diya_window_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ DiyaWindow * self = DIYA_WINDOW(object);
+ switch (property_id)
+ {
+ case WIN_APP_ID:
+ g_value_set_string(value, self->appid);
+ break;
+ case WIN_TITLE:
+ g_value_set_string(value, self->title);
+ break;
+ case WIN_HANDLE:
+ g_value_set_pointer(value, self->handle);
+ break;
+ case WIN_PARENT:
+ g_value_set_pointer(value, self->parent_win);
+ break;
+ case SHELL:
+ g_value_set_pointer(value, self->shell);
+ break;
+ case WIN_STATE:
+ g_value_set_uint(value, self->state);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void diya_window_init(DiyaWindow *self)
+{
+ self->appid = NULL;
+ //self->handle = NULL;
+ self->parent_win = NULL;
+ //self->shell = NULL;
+ self->state = DIYA_WIN_STATE_NONE;
+ self->title = NULL;
+}
+
+static const gchar* diya_window_to_string(DiyaObject* object)
+{
+ DiyaWindow* self = DIYA_WINDOW(object);
+ g_snprintf(self->string, sizeof(self->string), "Window 0x%" PRIXPTR ": %s (%s)", (uintptr_t) self->handle, self->appid?self->appid:"", self->title?self->title:"");
+ return self->string;
+}
+
+static void diya_window_class_init(DiyaWindowClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+ DiyaObjectClass *base_class = DIYA_OBJECT_CLASS(class);
+
+ gobject_class->finalize = diya_window_finalize;
+ gobject_class->set_property = diya_window_set_property;
+ gobject_class->get_property = diya_window_get_property;
+ base_class->to_string = diya_window_to_string;
+
+ win_properties[WIN_APP_ID] = g_param_spec_string("appid", NULL, "Window application id", "", G_PARAM_READWRITE);
+ win_properties[WIN_TITLE] = g_param_spec_string("title", NULL, "Window title","", G_PARAM_READWRITE );
+ win_properties[WIN_HANDLE] = g_param_spec_pointer("handle", NULL, "Foreign window handle", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY );
+ win_properties[WIN_STATE] = g_param_spec_uint("state", NULL, "Window state",0, UINT_MAX , DIYA_WIN_STATE_NONE,G_PARAM_READWRITE);
+ win_properties[WIN_PARENT] = g_param_spec_pointer("parent", NULL, "Window parent", G_PARAM_READWRITE);
+ win_properties[SHELL] = g_param_spec_pointer("shell", NULL, "Reference to shell", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY );
+
+ g_object_class_install_properties (gobject_class, N_PROPERTIES, win_properties);
+}
+
+static struct zwlr_foreign_toplevel_manager_v1 *g_toplevel_manager;
+
+static void toplevel_handle_output_leave(void *data,
+ struct zwlr_foreign_toplevel_handle_v1 *handle,
+ struct wl_output *output)
+{
+ (void) data;
+ (void) handle;
+ (void) output;
+}
+static void toplevel_handle_title(void *data,
+ struct zwlr_foreign_toplevel_handle_v1 *handle,
+ const char *title)
+{
+ DiyaShell *shell = (DiyaShell *)data;
+ assert(shell);
+ DiyaWindow *win = diya_shell_get_window(shell, handle);
+ assert(win);
+ g_object_set(win, "title", title, NULL);
+ g_debug("New title for: %s", diya_object_to_string(win));
+}
+static void toplevel_handle_app_id(void *data,
+ struct zwlr_foreign_toplevel_handle_v1 *handle,
+ const char *app_id)
+{
+ DiyaShell *shell = (DiyaShell *)data;
+ assert(shell);
+ DiyaWindow *win = diya_shell_get_window(shell, handle);
+ assert(win);
+ g_object_set(win, "appid", app_id, NULL);
+ g_debug("New appid for: %s", diya_object_to_string(win));
+}
+static void toplevel_handle_output_enter(void *data,
+ struct zwlr_foreign_toplevel_handle_v1 *handle,
+ struct wl_output *output)
+{
+ (void) data;
+ (void) handle;
+ (void) output;
+}
+static void toplevel_handle_state(void *data,
+ struct zwlr_foreign_toplevel_handle_v1 *handle,
+ struct wl_array *state)
+{
+ uint32_t *entry;
+ DiyaShell *shell = (DiyaShell *)data;
+ assert(shell);
+ DiyaWindow *win = diya_shell_get_window(shell, handle);
+ assert(win);
+ enum diya_win_state wstate = DIYA_WIN_STATE_NONE;
+ wl_array_for_each(entry, state) switch (*entry)
+ {
+ case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED:
+ wstate |= DIYA_WIN_STATE_MINIMIZE;
+ break;
+ case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED:
+ wstate |= DIYA_WIN_STATE_MAXIMIZE;
+ break;
+ case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN:
+ wstate |= DIYA_WIN_STATE_FULLSCREEN;
+ break;
+ case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED:
+ wstate |= DIYA_WIN_STATE_FOCUS;
+ break;
+ }
+ g_object_set(win, "state", wstate, NULL);
+}
+static void toplevel_handle_done(void *data,
+ struct zwlr_foreign_toplevel_handle_v1 *handle)
+{
+ DiyaShell *shell = (DiyaShell *)data;
+ assert(shell);
+ DiyaWindow *win = diya_shell_get_window(shell, handle);
+ assert(win);
+ g_signal_emit_by_name(shell, "foreign-window-changed", (void *)win);
+}
+static void toplevel_handle_closed(void *data,
+ struct zwlr_foreign_toplevel_handle_v1 *handle)
+{
+ DiyaShell *shell = (DiyaShell *)data;
+ assert(shell);
+ DiyaWindow *win = diya_shell_get_window(shell, handle);
+ assert(win);
+ g_signal_emit_by_name(shell, "foreign-window-removed", (void *)win);
+ diya_shell_remove_window(shell, win);
+ zwlr_foreign_toplevel_handle_v1_destroy(handle);
+}
+static void toplevel_handle_parent(void *data,
+ struct zwlr_foreign_toplevel_handle_v1 *handle,
+ struct zwlr_foreign_toplevel_handle_v1 *parent)
+{
+ if (!parent)
+ {
+ return;
+ }
+ assert(handle != parent);
+ DiyaShell *shell = (DiyaShell *)data;
+ assert(shell);
+ DiyaWindow *child_win = diya_shell_get_window(shell, handle);
+ DiyaWindow *parent_win = diya_shell_get_window(shell, parent);
+ assert(child_win);
+ assert(parent_win);
+ assert(child_win != parent_win);
+ g_object_set(child_win, "parent", parent_win, NULL);
+ g_debug("toplevel_handle_parent: %s is child of %s",
+ diya_object_to_string(child_win),
+ diya_object_to_string(parent_win));
+}
+static const struct zwlr_foreign_toplevel_handle_v1_listener g_toplevel_impl = {
+ .title = toplevel_handle_title,
+ .app_id = toplevel_handle_app_id,
+ .output_enter = toplevel_handle_output_enter,
+ .output_leave = toplevel_handle_output_leave,
+ .state = toplevel_handle_state,
+ .done = toplevel_handle_done,
+ .closed = toplevel_handle_closed,
+ .parent = toplevel_handle_parent};
+
+static void toplevel_manager_handle_toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager, struct zwlr_foreign_toplevel_handle_v1 *tl)
+{
+ (void) toplevel_manager;
+ DiyaShell *shell = (DiyaShell *)data;
+ assert(shell);
+ DiyaWindow *win = diya_shell_get_window(shell, tl);
+
+ if (win)
+ {
+ g_debug("[%s] already exists",diya_object_to_string(win));
+ return;
+ }
+ // TODO: different between windows
+ win = diya_window_new(shell, tl);
+ zwlr_foreign_toplevel_handle_v1_add_listener(tl, &g_toplevel_impl, data);
+}
+
+static void toplevel_manager_handle_finished(void *data,
+ struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager)
+{
+ (void) data;
+ // remove table entry
+ zwlr_foreign_toplevel_manager_v1_destroy(toplevel_manager);
+}
+
+static const struct zwlr_foreign_toplevel_manager_v1_listener g_toplevel_manager_impl =
+ {
+ .toplevel = toplevel_manager_handle_toplevel,
+ .finished = toplevel_manager_handle_finished,
+};
+
+void diya_shell_foreign_toplevel_register(struct wl_registry *registry, uint32_t name, DiyaShell *shell)
+{
+ g_toplevel_manager = wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface, 3);
+ zwlr_foreign_toplevel_manager_v1_add_listener(g_toplevel_manager, &g_toplevel_manager_impl, (void *)shell);
+}
+
+DiyaWindow * diya_window_new(DiyaShell* shell, gpointer handle)
+{
+ DiyaWindow *win = DIYA_WINDOW(g_object_new(DIYA_TYPE_WINDOW,"shell", shell, "handle", handle, NULL));
+ diya_shell_add_window(shell, win);
+ g_debug("Add new window 0x%" PRIXPTR, (uintptr_t)handle);
+ return win;
+}
\ No newline at end of file
diff --git a/src/foreign.h b/src/foreign.h
new file mode 100644
index 0000000..1a370ca
--- /dev/null
+++ b/src/foreign.h
@@ -0,0 +1,7 @@
+#ifndef DIYA_SHELL_FOREIGN_H
+#define DIYA_SHELL_FOREIGN_H
+#include "wlr-foreign-toplevel-management-unstable-v1.h"
+#include "shell.h"
+
+void diya_shell_foreign_toplevel_register(struct wl_registry *registry, uint32_t name, DiyaShell * shell);
+#endif
\ No newline at end of file
diff --git a/src/launcher.c b/src/launcher.c
new file mode 100644
index 0000000..40a7a60
--- /dev/null
+++ b/src/launcher.c
@@ -0,0 +1,78 @@
+#include "launcher.h"
+#include "foreign.h"
+#include
+
+#define NAMESPACE "launcher"
+
+static void on_launcher_destroy(GtkWindow *window, GApplication *_data)
+{
+ (void) window;
+ (void)_data;
+ //g_application_quit (G_APPLICATION (gtk_window_get_application (window)));
+}
+
+static void on_foreign_window_change(GtkApplication* app, DiyaWindow * win, gpointer data)
+{
+ (void) app;
+ (void) data;
+ assert(win);
+ g_warning("WINDOW CHANGEEEEEEEEE %s", diya_object_to_string(DIYA_OBJECT(win)));
+}
+
+static void on_foreign_window_removed(GtkApplication* app, DiyaWindow * win, gpointer data)
+{
+ (void) app;
+ (void) data;
+ assert(win);
+ g_warning("WINDOW removed %s", diya_object_to_string(DIYA_OBJECT(win)));
+}
+
+void diya_launcher_init(DiyaShell * shell)
+{
+ assert(shell);
+ GtkWindow *gtk_window;
+ g_object_get(shell, "launchpad", >k_window, NULL);
+ assert(gtk_window);
+ g_signal_connect (gtk_window, "destroy", G_CALLBACK (on_launcher_destroy), NULL);
+ // int layer shell for window
+ gtk_layer_init_for_window (gtk_window);
+ // anchor window to all edges
+ gtk_layer_set_anchor (gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, true);
+ gtk_layer_set_anchor (gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, true);
+ gtk_layer_set_anchor (gtk_window, GTK_LAYER_SHELL_EDGE_TOP, true);
+ gtk_layer_set_anchor (gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, false);
+
+ // set margin on window
+ for (int i = 0; i < GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER; i++)
+ gtk_layer_set_margin (gtk_window, i, 0);
+ gtk_layer_set_layer (gtk_window, GTK_LAYER_SHELL_LAYER_TOP);
+ gtk_layer_set_keyboard_mode (gtk_window, GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND);
+ gtk_layer_set_namespace (gtk_window, NAMESPACE);
+ // the top launcher shall be exclusive
+ gtk_layer_auto_exclusive_zone_enable (gtk_window);
+
+ gtk_widget_set_name(GTK_WIDGET(gtk_window),NAMESPACE);
+ gtk_window_set_default_size(gtk_window, 48, 48);
+
+ GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
+ gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
+ gtk_window_set_child (GTK_WINDOW (gtk_window), box);
+
+ //g_signal_connect (gtk_window, "orientation-changed", G_CALLBACK (on_orientation_changed), /*data*/NULL);
+ gtk_window_present (GTK_WINDOW (gtk_window));
+
+ g_signal_connect (shell, "foreign-window-changed", G_CALLBACK (on_foreign_window_change), NULL);
+ g_signal_connect (shell, "foreign-window-removed", G_CALLBACK (on_foreign_window_removed), NULL);
+
+ GList *apps = g_app_info_get_all ();
+ GList * l;
+ for (l = apps; l != NULL; l = l->next)
+ {
+ gpointer element_data = l->data;
+ g_warning("%s", g_app_info_get_display_name(element_data)); /*print out all of the display names of the .desktop files */
+ g_warning("%s", g_app_info_get_id(element_data));
+ g_warning("%s", g_app_info_get_name(element_data));
+ //g_warning("%s", g_app_info_get_icon(element_data));
+ }
+ g_list_free_full(apps, g_object_unref);
+}
\ No newline at end of file
diff --git a/src/launcher.h b/src/launcher.h
new file mode 100644
index 0000000..c7059f1
--- /dev/null
+++ b/src/launcher.h
@@ -0,0 +1,9 @@
+#ifndef DIYA_SHELL_LAUNCHER_H
+#define DIYA_SHELL_LAUNCHER_H
+
+#include "shell.h"
+
+
+void diya_launcher_init(DiyaShell * shell);
+
+#endif
\ No newline at end of file
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..e69de29
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..e69de29
diff --git a/src/shell.c b/src/shell.c
new file mode 100644
index 0000000..a04ec0c
--- /dev/null
+++ b/src/shell.c
@@ -0,0 +1,230 @@
+#include
+
+#include "background.h"
+#include "launcher.h"
+#include "wayland.h"
+#include "foreign.h"
+
+#define SHELL_DESCRIPTION "Diya GTK shell for wayland (diyac)"
+
+enum
+{
+ NO_PROP,
+ SHELL_APP,
+ SHELL_BACKGROUND_WIDGET,
+ SHELL_LAUNCHPAD_WIDGET,
+ SHELL_WINDOWS,
+ N_PROPERTIES
+};
+
+static GParamSpec *shell_properties[N_PROPERTIES] = {0};
+
+struct _DiyaShell
+{
+ DiyaObject parent;
+ GtkApplication* app;
+ GtkWindow* background;
+ GtkWindow* launcher;
+ GHashTable* windows;
+};
+G_DEFINE_TYPE(DiyaShell, diya_shell, DIYA_TYPE_OBJECT)
+
+static void diya_shell_finalize(GObject* object)
+{
+ DiyaShell * self = DIYA_SHELL(object);
+ g_hash_table_destroy(self->windows);
+ // TODO: free element in each entry of the table
+ // g_object_unref(self->app);
+ g_warning("diya_shell_finalize");
+}
+
+static void diya_shell_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ DiyaShell * self = DIYA_SHELL(object);
+ switch (property_id)
+ {
+ case SHELL_APP:
+ self->app = g_value_get_pointer(value);
+ assert(self->app);
+ self->background = GTK_WINDOW (gtk_application_window_new (self->app));
+ self->launcher = GTK_WINDOW (gtk_application_window_new (self->app));
+ break;
+ case SHELL_BACKGROUND_WIDGET:
+ case SHELL_LAUNCHPAD_WIDGET:
+ case SHELL_WINDOWS:
+ //self->windows = g_value_get_pointer(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void diya_shell_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ DiyaShell * self = DIYA_SHELL(object);
+ switch (property_id)
+ {
+ case SHELL_APP:
+ g_value_set_pointer(value, self->app);
+ break;
+ case SHELL_BACKGROUND_WIDGET:
+ g_value_set_pointer(value, self->background);
+ break;
+ case SHELL_LAUNCHPAD_WIDGET:
+ assert(self->launcher);
+ g_value_set_pointer(value, self->launcher);
+ break;
+ case SHELL_WINDOWS:
+ g_value_set_pointer(value, self->windows);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static const gchar* diya_shell_to_string(DiyaObject* object)
+{
+ (void) object;
+ return SHELL_DESCRIPTION;
+}
+
+static void diya_shell_class_init(DiyaShellClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+ DiyaObjectClass *base_class = DIYA_OBJECT_CLASS(class);
+
+ base_class->to_string = diya_shell_to_string;
+ gobject_class->finalize = diya_shell_finalize;
+ gobject_class->set_property = diya_shell_set_property;
+ gobject_class->get_property = diya_shell_get_property;
+
+ shell_properties[SHELL_APP] = g_param_spec_pointer("application", NULL, "Shell application", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ shell_properties[SHELL_BACKGROUND_WIDGET] = g_param_spec_pointer("background", NULL, "Shell background widget", G_PARAM_READABLE );
+ shell_properties[SHELL_LAUNCHPAD_WIDGET] = g_param_spec_pointer("launchpad", NULL, "Shell launchpad", G_PARAM_READABLE );
+ shell_properties[SHELL_WINDOWS] = g_param_spec_pointer("windows", NULL, "Shell foreign windows", G_PARAM_READABLE);
+ g_object_class_install_properties (gobject_class, N_PROPERTIES, shell_properties);
+
+ g_signal_new("foreign-window-changed",
+ DIYA_TYPE_SHELL,
+ G_SIGNAL_DETAILED |
+ G_SIGNAL_ACTION |
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+ g_signal_new("foreign-window-removed",
+ DIYA_TYPE_SHELL,
+ G_SIGNAL_DETAILED |
+ G_SIGNAL_ACTION |
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER);
+}
+
+static void diya_shell_init(DiyaShell *self)
+{
+ //self->app = NULL;
+ self->background = NULL;
+ self->launcher = NULL;
+ self->windows = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref);
+}
+
+gpointer diya_shell_get_window(DiyaShell * shell, gpointer handle)
+{
+ return g_hash_table_lookup(shell->windows, handle);
+}
+gboolean diya_shell_add_window(DiyaShell * shell, DiyaWindow * win)
+{
+ gpointer handle;
+ g_object_get(win, "handle", &handle, NULL);
+ assert(handle);
+ return g_hash_table_insert(shell->windows,handle, win);
+}
+
+gboolean diya_shell_remove_window(DiyaShell * shell, DiyaWindow * win)
+{
+ gpointer handle;
+ g_object_get(win, "handle", &handle, NULL);
+ assert(handle);
+ return g_hash_table_remove(shell->windows,handle);
+}
+
+DiyaShell * diya_shell_new(GtkApplication * app)
+{
+ return DIYA_SHELL(g_object_new(DIYA_TYPE_SHELL,"application",app, NULL));
+}
+/*
+static void on_orientation_changed (GtkWindow *window, WindowOrientation orientation, ToplevelData *data)
+{
+ (void)window;
+ GtkOrientation orient_toplevel, orient_sub;
+ orient_toplevel = GTK_ORIENTATION_HORIZONTAL;
+ orient_sub = GTK_ORIENTATION_VERTICAL;
+
+ switch (orientation) {
+ case WINDOW_ORIENTATION_HORIZONTAL:
+ orient_toplevel = GTK_ORIENTATION_HORIZONTAL;
+ orient_sub = GTK_ORIENTATION_HORIZONTAL;
+ break;
+ case WINDOW_ORIENTATION_VERTICAL:
+ orient_toplevel = GTK_ORIENTATION_VERTICAL;
+ orient_sub = GTK_ORIENTATION_VERTICAL;
+ break;
+ case WINDOW_ORIENTATION_NONE:
+ orient_toplevel = GTK_ORIENTATION_HORIZONTAL;
+ orient_sub = GTK_ORIENTATION_VERTICAL;
+ break;
+ }
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (data->toplevel_box), orient_toplevel);
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (data->first_box), orient_sub);
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (data->second_box), orient_sub);
+ //gtk_window_resize (window, 1, 1); // force the window to shrink to the smallest size it can
+}
+*/
+static void shutdown(GtkApplication *app, void *data)
+{
+ (void) app;
+ g_warning("Application shutdown");
+ DiyaShell * shell = data;
+ assert(shell);
+ g_object_unref(shell);
+}
+static void activate(GtkApplication *app, void *data)
+{
+ (void)data;
+ DiyaShell * shell = diya_shell_new(app);
+ assert(shell);
+ assert(DIYA_IS_SHELL(shell));
+ g_signal_connect(app, "shutdown", G_CALLBACK(shutdown), (void*)shell);
+
+ diya_launcher_init(shell);
+ diya_shell_wayland_init(shell);
+ diya_shell_init_background(shell);
+ // CSS support
+ // set color for it
+ GtkCssProvider *provider = gtk_css_provider_new();
+ const char *css = "#launcher {background-color: red;} #background {background-image:url(\"file:///etc/xdg/labwc/wpp.jpg\");background-size: cover;}";
+ gtk_css_provider_load_from_string(provider, css);
+ gtk_style_context_add_provider_for_display(
+ gdk_display_get_default(), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
+}
+
+int main(int argc, char *argv[])
+{
+ GtkApplication* app = gtk_application_new("dev.iohub.diya-shell", G_APPLICATION_DEFAULT_FLAGS);
+ g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
+ int status = g_application_run(G_APPLICATION(app), argc, argv);
+ g_object_unref(app);
+ return status;
+}
diff --git a/src/shell.h b/src/shell.h
new file mode 100644
index 0000000..1ba6234
--- /dev/null
+++ b/src/shell.h
@@ -0,0 +1,27 @@
+#ifndef DIYA_SHELL_H
+#define DIYA_SHELL_H
+#include
+#include "base.h"
+
+enum diya_win_state
+{
+ DIYA_WIN_STATE_NONE = 0,
+ DIYA_WIN_STATE_MINIMIZE = 1 << 0,
+ DIYA_WIN_STATE_MAXIMIZE = 1 << 1,
+ DIYA_WIN_STATE_FULLSCREEN = 1 << 2,
+ DIYA_WIN_STATE_FOCUS = 1 << 3,
+};
+
+#define DIYA_TYPE_SHELL (diya_shell_get_type ())
+G_DECLARE_FINAL_TYPE (DiyaShell, diya_shell, DIYA, SHELL, DiyaObject)
+
+#define DIYA_TYPE_WINDOW (diya_window_get_type ())
+G_DECLARE_FINAL_TYPE (DiyaWindow, diya_window, DIYA, WINDOW, DiyaObject)
+
+gpointer diya_shell_get_window(DiyaShell * shell, gpointer handle);
+gboolean diya_shell_add_window(DiyaShell * shell, DiyaWindow * win);
+gboolean diya_shell_remove_window(DiyaShell * shell, DiyaWindow * win);
+DiyaShell * diya_shell_new(GtkApplication * app);
+
+DiyaWindow * diya_window_new(DiyaShell* shell, gpointer handle);
+#endif
\ No newline at end of file
diff --git a/src/wayland.c b/src/wayland.c
new file mode 100644
index 0000000..58dc171
--- /dev/null
+++ b/src/wayland.c
@@ -0,0 +1,57 @@
+#include
+#include
+#include "wayland.h"
+#include "foreign.h"
+
+static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const gchar *interface, uint32_t version)
+{
+ /*if (!g_strcmp0(interface, zxdg_output_manager_v1_interface.name))
+ xdg_output_register(registry, name);
+ else if (!g_strcmp0(interface, wl_shm_interface.name))
+ shm_register(registry, name);
+ else if (!g_strcmp0(interface, zwlr_layer_shell_v1_interface.name))
+ layer_shell_register(registry, name, version);
+ if (!g_strcmp0(interface,zwlr_foreign_toplevel_manager_v1_interface.name))
+ foreign_toplevel_register(registry,name);
+ */
+ //g_warning("register global: %s VS %s", interface, zwlr_foreign_toplevel_manager_v1_interface.name);
+ (void) version;
+ if (!g_strcmp0(interface, zwlr_foreign_toplevel_manager_v1_interface.name))
+ {
+ g_warning("register global: %s", interface);
+ diya_shell_foreign_toplevel_register(registry, name, data);
+ }
+
+}
+
+static void handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+ (void) data;
+ (void) registry;
+ (void) name;
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = handle_global_remove
+};
+
+void diya_shell_wayland_init(DiyaShell *shell)
+{
+ struct wl_display *display;
+ struct wl_registry *registry;
+ display = gdk_wayland_display_get_wl_display(gdk_display_get_default());
+ if (!display)
+ g_error("Can't get wayland display\n");
+
+ registry = wl_display_get_registry(display);
+ wl_registry_add_listener(registry, ®istry_listener, (void*)shell);
+ wl_display_roundtrip(display);
+ // wayland_monitor_probe();
+ // GdkMonitor *mon = wayland_monitor_get_default();
+ // g_info("default output: %s", (gchar *)g_object_get_data(G_OBJECT(mon), "xdg_name"));
+
+ // wl_display_roundtrip(display);
+ // wl_display_roundtrip(display);
+}
diff --git a/src/wayland.h b/src/wayland.h
new file mode 100644
index 0000000..7e46cf6
--- /dev/null
+++ b/src/wayland.h
@@ -0,0 +1,8 @@
+#ifndef DIYA_SHELL_WAYLAND_H
+#define DIYA_SHELL_WAYLAND_H
+
+#include "shell.h"
+
+void diya_shell_wayland_init(DiyaShell *shell);
+
+#endif
\ No newline at end of file