[console] watcher: split watcher out from application



commit 004e034f80407ca919e72c8708c9c5ab71ff37c2
Author: Zander Brown <zbrown gnome org>
Date:   Sat Aug 6 02:54:37 2022 +0100

    watcher: split watcher out from application
    
    At least initally it takes the form of a global singleton, but will
    probably be attached to application again in due course
    
    The watcher machinery didn't really interact with much of application,
    and didn't really conceptually belong there. By splitting out we
    hopefully simplify later experiments with alternative watchers.

 src/kgx-application.c | 234 +----------------------------------
 src/kgx-application.h |  28 -----
 src/kgx-watcher.c     | 336 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/kgx-watcher.h     |  41 ++++++
 src/kgx-window.c      |   9 +-
 src/meson.build       |   2 +
 6 files changed, 384 insertions(+), 266 deletions(-)
---
diff --git a/src/kgx-application.c b/src/kgx-application.c
index f2a0f7f..0b61316 100644
--- a/src/kgx-application.c
+++ b/src/kgx-application.c
@@ -40,6 +40,7 @@
 #include "kgx-pages.h"
 #include "kgx-simple-tab.h"
 #include "kgx-resources.h"
+#include "kgx-watcher.h"
 
 #define LOGO_COL_SIZE 28
 #define LOGO_ROW_SIZE 14
@@ -172,8 +173,6 @@ kgx_application_finalize (GObject *object)
   g_clear_object (&self->settings);
   g_clear_object (&self->desktop_interface);
 
-  g_clear_pointer (&self->watching, g_tree_unref);
-  g_clear_pointer (&self->children, g_tree_unref);
   g_clear_pointer (&self->pages, g_tree_unref);
 
   G_OBJECT_CLASS (kgx_application_parent_class)->finalize (object);
@@ -205,115 +204,6 @@ kgx_application_activate (GApplication *app)
 }
 
 
-static gboolean
-handle_watch_iter (gpointer pid,
-                   gpointer val,
-                   gpointer user_data)
-{
-  KgxProcess *process = val;
-  KgxApplication *self = user_data;
-  GPid parent = kgx_process_get_parent (process);
-  struct ProcessWatch *watch = NULL;
-
-  watch = g_tree_lookup (self->watching, GINT_TO_POINTER (parent));
-
-  // There are far more processes on the system than there are children
-  // of watches, thus lookup are unlikly
-  if (G_UNLIKELY (watch != NULL)) {
-
-    /* If the page died we stop caring about its processes */
-    if (G_UNLIKELY (watch->page == NULL)) {
-      g_tree_remove (self->watching, GINT_TO_POINTER (parent));
-      g_tree_remove (self->children, pid);
-
-      return FALSE;
-    }
-
-    if (!g_tree_lookup (self->children, pid)) {
-      struct ProcessWatch *child_watch = g_new0 (struct ProcessWatch, 1);
-
-      child_watch->process = g_rc_box_acquire (process);
-      g_set_weak_pointer (&child_watch->page, watch->page);
-
-      g_debug ("Hello %i!", GPOINTER_TO_INT (pid));
-
-      g_tree_insert (self->children, pid, child_watch);
-    }
-
-    kgx_tab_push_child (watch->page, process);
-  }
-
-  return FALSE;
-}
-
-
-struct RemoveDead {
-  GTree     *plist;
-  GPtrArray *dead;
-};
-
-
-static gboolean
-remove_dead (gpointer pid,
-             gpointer val,
-             gpointer user_data)
-{
-  struct RemoveDead *data = user_data;
-  struct ProcessWatch *watch = val;
-
-  if (!g_tree_lookup (data->plist, pid)) {
-    g_debug ("%i marked as dead", GPOINTER_TO_INT (pid));
-
-    kgx_tab_pop_child (watch->page, watch->process);
-
-    g_ptr_array_add (data->dead, pid);
-  }
-
-  return FALSE;
-}
-
-
-static gboolean
-watch (gpointer data)
-{
-  KgxApplication *self = KGX_APPLICATION (data);
-  g_autoptr (GTree) plist = NULL;
-  struct RemoveDead dead;
-
-  plist = kgx_process_get_list ();
-
-  g_tree_foreach (plist, handle_watch_iter, self);
-
-  dead.plist = plist;
-  dead.dead = g_ptr_array_new_full (1, NULL);
-
-  g_tree_foreach (self->children, remove_dead, &dead);
-
-  // We can't modify self->chilren whilst walking it
-  for (int i = 0; i < dead.dead->len; i++) {
-    g_tree_remove (self->children, g_ptr_array_index (dead.dead, i));
-  }
-
-  g_ptr_array_unref (dead.dead);
-
-  return G_SOURCE_CONTINUE;
-}
-
-static inline void
-set_watcher (KgxApplication *self, gboolean focused)
-{
-  g_debug ("updated watcher focused? %s", focused ? "yes" : "no");
-
-  if (self->timeout != 0) {
-    g_source_remove (self->timeout);
-  }
-
-  // Slow down polling when nothing is focused
-  self->timeout = g_timeout_add (focused ? 500 : 2000, watch, self);
-  g_source_set_name_by_id (self->timeout, "[kgx] child watcher");
-}
-
-
 static void
 kgx_application_startup (GApplication *app)
 {
@@ -365,8 +255,6 @@ kgx_application_startup (GApplication *app)
 
   settings_action = g_settings_create_action (self->settings, "theme");
   g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (settings_action));
-
-  set_watcher (KGX_APPLICATION (app), TRUE);
 }
 
 
@@ -681,18 +569,6 @@ kgx_application_class_init (KgxApplicationClass *klass)
 }
 
 
-static void
-clear_watch (struct ProcessWatch *watch)
-{
-  g_return_if_fail (watch != NULL);
-
-  g_clear_pointer (&watch->process, kgx_process_unref);
-  g_clear_weak_pointer (&watch->page);
-
-  g_clear_pointer (&watch, g_free);
-}
-
-
 static void
 font_changed (GSettings      *settings,
               const char     *key,
@@ -914,67 +790,7 @@ kgx_application_init (KgxApplication *self)
                     G_CALLBACK (font_changed),
                     self);
 
-  self->watching = g_tree_new_full (kgx_pid_cmp,
-                                    NULL,
-                                    NULL,
-                                    (GDestroyNotify) clear_watch);
-  self->children = g_tree_new_full (kgx_pid_cmp,
-                                    NULL,
-                                    NULL,
-                                    (GDestroyNotify) clear_watch);
   self->pages = g_tree_new_full (kgx_pid_cmp, NULL, NULL, NULL);
-
-  self->active = 0;
-  self->timeout = 0;
-}
-
-
-/**
- * kgx_application_add_watch:
- * @self: the #KgxApplication
- * @pid: the shell process to watch
- * @page: the #KgxTab the shell is running in
- *
- * Registers a new shell process with the pid watcher
- */
-void
-kgx_application_add_watch (KgxApplication *self,
-                           GPid            pid,
-                           KgxTab        *page)
-{
-  struct ProcessWatch *watch;
-
-  g_return_if_fail (KGX_IS_APPLICATION (self));
-  g_return_if_fail (KGX_IS_TAB (page));
-
-  watch = g_new0 (struct ProcessWatch, 1);
-  watch->process = kgx_process_new (pid);
-  g_set_weak_pointer (&watch->page, page);
-
-  g_debug ("Started watching %i", pid);
-
-  g_tree_insert (self->watching, GINT_TO_POINTER (pid), watch);
-}
-
-/**
- * kgx_application_remove_watch:
- * @self: the #KgxApplication
- * @pid: the shell process to stop watch watching
- *
- * unregisters the shell with #GPid pid
- */
-void
-kgx_application_remove_watch (KgxApplication *self,
-                              GPid            pid)
-{
-  g_return_if_fail (KGX_IS_APPLICATION (self));
-
-  if (G_LIKELY (g_tree_lookup (self->watching, GINT_TO_POINTER (pid)))) {
-    g_tree_remove (self->watching, GINT_TO_POINTER (pid));
-    g_debug ("Stopped watching %i", pid);
-  } else {
-    g_warning ("Unknown process %i", pid);
-  }
 }
 
 
@@ -1001,52 +817,6 @@ kgx_application_get_font (KgxApplication *self)
 }
 
 
-/**
- * kgx_application_push_active:
- * @self: the #KgxApplication
- *
- * Increase the active window count
- */
-void
-kgx_application_push_active (KgxApplication *self)
-{
-  g_return_if_fail (KGX_IS_APPLICATION (self));
-
-  self->active++;
-
-  g_debug ("push_active");
-
-  if (G_LIKELY (self->active > 0)) {
-    set_watcher (self, TRUE);
-  } else {
-    set_watcher (self, FALSE);
-  }
-}
-
-
-/**
- * kgx_application_pop_active:
- * @self: the #KgxApplication
- *
- * Decrease the active window count
- */
-void
-kgx_application_pop_active (KgxApplication *self)
-{
-  g_return_if_fail (KGX_IS_APPLICATION (self));
-
-  self->active--;
-
-  g_debug ("pop_active");
-
-  if (G_LIKELY (self->active < 1)) {
-    set_watcher (self, FALSE);
-  } else {
-    set_watcher (self, TRUE);
-  }
-}
-
-
 static void
 page_died (gpointer data, GObject *dead_object)
 {
@@ -1117,7 +887,7 @@ started (GObject      *src,
     return;
   }
 
-  kgx_application_add_watch (KGX_APPLICATION (app), pid, page);
+  kgx_watcher_add (kgx_watcher_get_default (), pid, page);
 }
 
 
diff --git a/src/kgx-application.h b/src/kgx-application.h
index fa401cd..4aa879b 100644
--- a/src/kgx-application.h
+++ b/src/kgx-application.h
@@ -44,29 +44,13 @@ G_BEGIN_DECLS
 
 #define KGX_TYPE_APPLICATION (kgx_application_get_type())
 
-/**
- * ProcessWatch:
- * @page: the #KgxTab the #KgxProcess is in
- * @process: what we are watching
- *
- * Stability: Private
- */
-struct ProcessWatch {
-  KgxTab /*weak*/ *page;
-  KgxProcess *process;
-};
 
 /**
  * KgxApplication:
  * @theme: the colour palette in use
  * @scale: the font scaling used
  * @desktop_interface: the #GSettings storing the system monospace font
- * @watching: ~ (element-type GLib.Pid ProcessWatch) the shells running in windows
- * @children: ~ (element-type GLib.Pid ProcessWatch) the processes running in shells
  * @pages: ~ (element-type uint Kgx.Page) the global page id / page map
- * @active: counter of #KgxWindow's with #GtkWindow:is-active = %TRUE,
- *          obviously this should only ever be 1 or but we can't be certain
- * @timeout: the current #GSource id of the watcher
  *
  * Stability: Private
  */
@@ -83,25 +67,13 @@ struct _KgxApplication
   GSettings                *settings;
   GSettings                *desktop_interface;
 
-  GTree                    *watching;
-  GTree                    *children;
   GTree                    *pages;
-
-  guint                     timeout;
-  int                       active;
 };
 
 G_DECLARE_FINAL_TYPE (KgxApplication, kgx_application, KGX, APPLICATION, AdwApplication)
 
 
-void                  kgx_application_add_watch       (KgxApplication *self,
-                                                       GPid            pid,
-                                                       KgxTab         *page);
-void                  kgx_application_remove_watch    (KgxApplication *self,
-                                                       GPid            pid);
 PangoFontDescription *kgx_application_get_font        (KgxApplication *self);
-void                  kgx_application_push_active     (KgxApplication *self);
-void                  kgx_application_pop_active      (KgxApplication *self);
 void                  kgx_application_add_page        (KgxApplication *self,
                                                        KgxTab         *page);
 KgxTab               *kgx_application_lookup_page     (KgxApplication *self,
diff --git a/src/kgx-watcher.c b/src/kgx-watcher.c
new file mode 100644
index 0000000..2fdb966
--- /dev/null
+++ b/src/kgx-watcher.c
@@ -0,0 +1,336 @@
+/* kgx-watcher.c
+ *
+ * Copyright 2022 Zander Brown
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "kgx-config.h"
+
+#include "kgx-watcher.h"
+
+
+/**
+ * ProcessWatch:
+ * @page: the #KgxTab the #KgxProcess is in
+ * @process: what we are watching
+ *
+ * Stability: Private
+ */
+struct ProcessWatch {
+  KgxTab /*weak*/ *page;
+  KgxProcess *process;
+};
+
+
+/**
+ * KgxWatcher:
+ * @watching: (element-type GLib.Pid ProcessWatch) the shells running in windows
+ * @children: (element-type GLib.Pid ProcessWatch) the processes running in shells
+ * @active: counter of #KgxWindow's with #GtkWindow:is-active = %TRUE,
+ *          obviously this should only ever be 1 or but we can't be certain
+ * @timeout: the current #GSource id of the watcher
+ * 
+ * Used to monitor processes running in pages
+ */
+struct _KgxWatcher {
+  GObject                   parent_instance;
+
+  GTree                    *watching;
+  GTree                    *children;
+
+  guint                     timeout;
+  int                       active;
+};
+
+
+G_DEFINE_TYPE (KgxWatcher, kgx_watcher, G_TYPE_OBJECT)
+
+
+static void
+kgx_watcher_dispose (GObject *object)
+{
+  KgxWatcher *self = KGX_WATCHER (object);
+
+  g_clear_pointer (&self->watching, g_tree_unref);
+  g_clear_pointer (&self->children, g_tree_unref);
+
+  G_OBJECT_CLASS (kgx_watcher_parent_class)->dispose (object);
+}
+
+
+static void
+kgx_watcher_class_init (KgxWatcherClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = kgx_watcher_dispose;
+}
+
+
+static void
+clear_watch (struct ProcessWatch *watch)
+{
+  g_return_if_fail (watch != NULL);
+
+  g_clear_pointer (&watch->process, kgx_process_unref);
+  g_clear_weak_pointer (&watch->page);
+
+  g_clear_pointer (&watch, g_free);
+}
+
+
+static gboolean
+handle_watch_iter (gpointer pid,
+                   gpointer val,
+                   gpointer user_data)
+{
+  KgxProcess *process = val;
+  KgxWatcher *self = user_data;
+  GPid parent = kgx_process_get_parent (process);
+  struct ProcessWatch *watch = NULL;
+
+  watch = g_tree_lookup (self->watching, GINT_TO_POINTER (parent));
+
+  // There are far more processes on the system than there are children
+  // of watches, thus lookup are unlikly
+  if (G_UNLIKELY (watch != NULL)) {
+
+    /* If the page died we stop caring about its processes */
+    if (G_UNLIKELY (watch->page == NULL)) {
+      g_tree_remove (self->watching, GINT_TO_POINTER (parent));
+      g_tree_remove (self->children, pid);
+
+      return FALSE;
+    }
+
+    if (!g_tree_lookup (self->children, pid)) {
+      struct ProcessWatch *child_watch = g_new0 (struct ProcessWatch, 1);
+
+      child_watch->process = g_rc_box_acquire (process);
+      g_set_weak_pointer (&child_watch->page, watch->page);
+
+      g_debug ("Hello %i!", GPOINTER_TO_INT (pid));
+
+      g_tree_insert (self->children, pid, child_watch);
+    }
+
+    kgx_tab_push_child (watch->page, process);
+  }
+
+  return FALSE;
+}
+
+
+struct RemoveDead {
+  GTree     *plist;
+  GPtrArray *dead;
+};
+
+
+static gboolean
+remove_dead (gpointer pid,
+             gpointer val,
+             gpointer user_data)
+{
+  struct RemoveDead *data = user_data;
+  struct ProcessWatch *watch = val;
+
+  if (!g_tree_lookup (data->plist, pid)) {
+    g_debug ("%i marked as dead", GPOINTER_TO_INT (pid));
+
+    kgx_tab_pop_child (watch->page, watch->process);
+
+    g_ptr_array_add (data->dead, pid);
+  }
+
+  return FALSE;
+}
+
+
+static gboolean
+watch (gpointer data)
+{
+  KgxWatcher *self = KGX_WATCHER (data);
+  g_autoptr (GTree) plist = NULL;
+  struct RemoveDead dead;
+
+  plist = kgx_process_get_list ();
+
+  g_tree_foreach (plist, handle_watch_iter, self);
+
+  dead.plist = plist;
+  dead.dead = g_ptr_array_new_full (1, NULL);
+
+  g_tree_foreach (self->children, remove_dead, &dead);
+
+  // We can't modify self->chilren whilst walking it
+  for (int i = 0; i < dead.dead->len; i++) {
+    g_tree_remove (self->children, g_ptr_array_index (dead.dead, i));
+  }
+
+  g_ptr_array_unref (dead.dead);
+
+  return G_SOURCE_CONTINUE;
+}
+
+
+static inline void
+set_watcher (KgxWatcher *self, gboolean focused)
+{
+  g_debug ("updated watcher focused? %s", focused ? "yes" : "no");
+
+  if (self->timeout != 0) {
+    g_source_remove (self->timeout);
+  }
+
+  // Slow down polling when nothing is focused
+  self->timeout = g_timeout_add (focused ? 500 : 2000, watch, self);
+  g_source_set_name_by_id (self->timeout, "[kgx] child watcher");
+}
+
+
+static void
+kgx_watcher_init (KgxWatcher *self)
+{
+  self->watching = g_tree_new_full (kgx_pid_cmp,
+                                    NULL,
+                                    NULL,
+                                    (GDestroyNotify) clear_watch);
+  self->children = g_tree_new_full (kgx_pid_cmp,
+                                    NULL,
+                                    NULL,
+                                    (GDestroyNotify) clear_watch);
+
+  self->active = 0;
+  self->timeout = 0;
+
+  set_watcher (self, TRUE);
+}
+
+
+/**
+ * kgx_watcher_get_default:
+ * 
+ * Returns: (transfer none): the #KgxWatcher singleton
+ */
+KgxWatcher *
+kgx_watcher_get_default (void)
+{ 
+  static KgxWatcher *instance;
+
+  if (instance == NULL) {
+    instance = g_object_new (KGX_TYPE_WATCHER, NULL);
+    g_object_add_weak_pointer (G_OBJECT (instance), (gpointer *) &instance);
+  }
+
+  return instance;
+}
+
+
+/**
+ * kgx_watcher_add:
+ * @self: the #KgxWatcher
+ * @pid: the shell process to watch
+ * @page: the #KgxTab the shell is running in
+ *
+ * Registers a new shell process with the pid watcher
+ */
+void
+kgx_watcher_add (KgxWatcher *self,
+                 GPid        pid,
+                 KgxTab     *page)
+{
+  struct ProcessWatch *watch;
+
+  g_return_if_fail (KGX_IS_WATCHER (self));
+  g_return_if_fail (KGX_IS_TAB (page));
+
+  watch = g_new0 (struct ProcessWatch, 1);
+  watch->process = kgx_process_new (pid);
+  g_set_weak_pointer (&watch->page, page);
+
+  g_debug ("Started watching %i", pid);
+
+  g_tree_insert (self->watching, GINT_TO_POINTER (pid), watch);
+}
+
+
+/**
+ * kgx_watcher_remove:
+ * @self: the #KgxWatcher
+ * @pid: the shell process to stop watch watching
+ *
+ * unregisters the shell with #GPid pid
+ */
+void
+kgx_watcher_remove (KgxWatcher *self,
+                    GPid        pid)
+{
+  g_return_if_fail (KGX_IS_WATCHER (self));
+
+  if (G_LIKELY (g_tree_lookup (self->watching, GINT_TO_POINTER (pid)))) {
+    g_tree_remove (self->watching, GINT_TO_POINTER (pid));
+    g_debug ("Stopped watching %i", pid);
+  } else {
+    g_warning ("Unknown process %i", pid);
+  }
+}
+
+
+/**
+ * kgx_watcher_push_active:
+ * @self: the #KgxWatcher
+ *
+ * Increase the active window count
+ */
+void
+kgx_watcher_push_active (KgxWatcher *self)
+{
+  g_return_if_fail (KGX_IS_WATCHER (self));
+
+  self->active++;
+
+  g_debug ("push_active");
+
+  if (G_LIKELY (self->active > 0)) {
+    set_watcher (self, TRUE);
+  } else {
+    set_watcher (self, FALSE);
+  }
+}
+
+
+/**
+ * kgx_watcher_pop_active:
+ * @self: the #KgxWatcher
+ *
+ * Decrease the active window count
+ */
+void
+kgx_watcher_pop_active (KgxWatcher *self)
+{
+  g_return_if_fail (KGX_IS_WATCHER (self));
+
+  self->active--;
+
+  g_debug ("pop_active");
+
+  if (G_LIKELY (self->active < 1)) {
+    set_watcher (self, FALSE);
+  } else {
+    set_watcher (self, TRUE);
+  }
+}
+
diff --git a/src/kgx-watcher.h b/src/kgx-watcher.h
new file mode 100644
index 0000000..e344dae
--- /dev/null
+++ b/src/kgx-watcher.h
@@ -0,0 +1,41 @@
+/* kgx-watcher.h
+ *
+ * Copyright 2022 Zander Brown
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#include "kgx-process.h"
+#include "kgx-tab.h"
+
+G_BEGIN_DECLS
+
+#define KGX_TYPE_WATCHER kgx_watcher_get_type ()
+G_DECLARE_FINAL_TYPE (KgxWatcher, kgx_watcher, KGX, WATCHER, GObject)
+
+
+KgxWatcher           *kgx_watcher_get_default       (void);
+void                  kgx_watcher_add               (KgxWatcher     *self,
+                                                     GPid            pid,
+                                                     KgxTab         *page);
+void                  kgx_watcher_remove            (KgxWatcher     *self,
+                                                     GPid            pid);
+void                  kgx_watcher_push_active       (KgxWatcher     *self);
+void                  kgx_watcher_pop_active        (KgxWatcher     *self);
+
+G_END_DECLS
diff --git a/src/kgx-window.c b/src/kgx-window.c
index 5086afe..9c63bb2 100644
--- a/src/kgx-window.c
+++ b/src/kgx-window.c
@@ -41,6 +41,7 @@
 #include "kgx-tab-button.h"
 #include "kgx-tab-switcher.h"
 #include "kgx-theme-switcher.h"
+#include "kgx-watcher.h"
 
 G_DEFINE_TYPE (KgxWindow, kgx_window, ADW_TYPE_APPLICATION_WINDOW)
 
@@ -211,14 +212,10 @@ kgx_window_close_request (GtkWindow *window)
 static void
 active_changed (GObject *object, GParamSpec *pspec, gpointer data)
 {
-  GtkApplication *app;
-
-  app = gtk_window_get_application (GTK_WINDOW (object));
-
   if (gtk_window_is_active (GTK_WINDOW (object))) {
-    kgx_application_push_active (KGX_APPLICATION (app));
+    kgx_watcher_push_active (kgx_watcher_get_default ());
   } else {
-    kgx_application_pop_active (KGX_APPLICATION (app));
+    kgx_watcher_pop_active (kgx_watcher_get_default ());
   }
 }
 
diff --git a/src/meson.build b/src/meson.build
index f8d5358..48a1dc2 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -23,6 +23,8 @@ kgx_sources = [
   'kgx-proxy-info.h',
   'kgx-close-dialog.c',
   'kgx-close-dialog.h',
+  'kgx-watcher.c',
+  'kgx-watcher.h',
   'kgx-window.c',
   'kgx-window.h',
   'kgx-process.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]