mirror of
https://github.com/elementary/gala.git
synced 2024-11-25 03:06:14 +01:00
Remember window position (#1751)
Co-authored-by: Corentin Noël <tintou@noel.tf> Co-authored-by: Danielle Foré <danielle@elementary.io> Co-authored-by: Ryan Kornheisl <ryan@skarva.tech>
This commit is contained in:
parent
4d70c8e3c5
commit
72b9e012f3
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
apt update
|
||||
apt install -y gettext gnome-settings-daemon-dev gsettings-desktop-schemas-dev libbamf3-dev libcanberra-dev libcanberra-gtk3-dev libclutter-1.0-dev libgee-0.8-dev libglib2.0-dev libgnome-desktop-3-dev libgranite-dev libgtk-3-dev ${{ matrix.mutter_pkg }} libplank-dev libxml2-utils libgexiv2-dev meson valac valadoc
|
||||
apt install -y gettext gnome-settings-daemon-dev gsettings-desktop-schemas-dev libbamf3-dev libcanberra-dev libcanberra-gtk3-dev libclutter-1.0-dev libgee-0.8-dev libglib2.0-dev libgnome-desktop-3-dev libgranite-dev libgtk-3-dev ${{ matrix.mutter_pkg }} libplank-dev libxml2-utils libgexiv2-dev libsqlite3-dev meson valac valadoc
|
||||
- name: Build
|
||||
env:
|
||||
DESTDIR: out
|
||||
|
@ -97,6 +97,7 @@ gsd_dep = dependency('gnome-settings-daemon', version: '>= @0@'.format(gsd_versi
|
||||
m_dep = cc.find_library('m', required: false)
|
||||
posix_dep = vala.find_library('posix', required: false)
|
||||
gexiv2_dep = dependency('gexiv2')
|
||||
sqlite3_dep = dependency('sqlite3')
|
||||
if get_option('systemd')
|
||||
systemd_dep = dependency('libsystemd')
|
||||
endif
|
||||
@ -163,7 +164,7 @@ endif
|
||||
add_project_arguments(vala_flags, language: 'vala')
|
||||
add_project_link_arguments(['-Wl,-rpath,@0@'.format(mutter_typelib_dir)], language: 'c')
|
||||
|
||||
gala_base_dep = [canberra_dep, glib_dep, gobject_dep, gio_dep, gio_unix_dep, gmodule_dep, gee_dep, gtk_dep, mutter_dep, granite_dep, gnome_desktop_dep, m_dep, posix_dep, gexiv2_dep, config_dep]
|
||||
gala_base_dep = [canberra_dep, glib_dep, gobject_dep, gio_dep, gio_unix_dep, gmodule_dep, gee_dep, gtk_dep, mutter_dep, granite_dep, gnome_desktop_dep, m_dep, posix_dep, gexiv2_dep, sqlite3_dep, config_dep]
|
||||
|
||||
if get_option('systemd')
|
||||
gala_base_dep += systemd_dep
|
||||
|
@ -187,6 +187,7 @@ namespace Gala {
|
||||
WindowListener.init (display);
|
||||
KeyboardManager.init (display);
|
||||
window_tracker = new WindowTracker ();
|
||||
WindowStateSaver.init (window_tracker);
|
||||
window_tracker.init (display);
|
||||
|
||||
notification_stack = new NotificationStack (display);
|
||||
@ -1436,6 +1437,8 @@ namespace Gala {
|
||||
public override void map (Meta.WindowActor actor) {
|
||||
unowned var window = actor.get_meta_window ();
|
||||
|
||||
WindowStateSaver.on_map (window);
|
||||
|
||||
if ((window.maximized_horizontally && behavior_settings.get_boolean ("move-maximized-workspace")) ||
|
||||
(window.fullscreen && window.is_on_primary_monitor () && behavior_settings.get_boolean ("move-fullscreened-workspace"))) {
|
||||
move_window_to_next_ws (window);
|
||||
|
164
src/WindowStateSaver.vala
Normal file
164
src/WindowStateSaver.vala
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2023 elementary, Inc. <https://elementary.io>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
public class Gala.WindowStateSaver : GLib.Object {
|
||||
private static unowned WindowTracker window_tracker;
|
||||
private static GLib.HashTable<string, GLib.Array<Meta.Window?>> app_windows;
|
||||
private static Sqlite.Database db;
|
||||
|
||||
public static void init (WindowTracker window_tracker) {
|
||||
WindowStateSaver.window_tracker = window_tracker;
|
||||
app_windows = new GLib.HashTable<string, GLib.Array<Meta.Window?>> (GLib.str_hash, GLib.str_equal);
|
||||
|
||||
var dir = Path.build_filename (GLib.Environment.get_user_data_dir (), "io.elementary.gala");
|
||||
Posix.mkdir (dir, 0775);
|
||||
var path = Path.build_filename (dir, "windowstate.db");
|
||||
var rc = Sqlite.Database.open_v2 (path, out db);
|
||||
|
||||
if (rc != Sqlite.OK) {
|
||||
critical ("Cannot open database: %d, %s", rc, db.errmsg ());
|
||||
return;
|
||||
}
|
||||
|
||||
Sqlite.Statement stmt;
|
||||
rc = db.prepare_v2 (
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS apps (
|
||||
app_id TEXT,
|
||||
window_index INTEGER,
|
||||
last_x INTEGER,
|
||||
last_y INTEGER,
|
||||
last_width INTEGER,
|
||||
last_height INTEGER,
|
||||
PRIMARY KEY (app_id, window_index)
|
||||
);
|
||||
""",
|
||||
-1, out stmt
|
||||
);
|
||||
|
||||
if (rc == Sqlite.OK) {
|
||||
rc = stmt.step ();
|
||||
if (rc == Sqlite.DONE) {
|
||||
// disable synchronized commits for performance reasons
|
||||
rc = db.exec ("PRAGMA synchronous=OFF");
|
||||
if (rc != Sqlite.OK) {
|
||||
warning ("Unable to disable synchronous mode %d, %s", rc, db.errmsg ());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
critical ("Cannot create table 'apps': %d, %s", rc, db.errmsg ());
|
||||
}
|
||||
|
||||
public static void on_map (Meta.Window window) {
|
||||
var app_id = GLib.Markup.escape_text (window_tracker.get_app_for_window (window).id);
|
||||
|
||||
if (app_id.has_prefix ("window:")) {
|
||||
// if window failed to be identified, don't remember it
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.window_type != Meta.WindowType.NORMAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(app_id in app_windows)) {
|
||||
app_windows[app_id] = new GLib.Array<Meta.Window?> ();
|
||||
}
|
||||
|
||||
var window_index = find_window_index (window, app_id);
|
||||
app_windows[app_id].insert_val (window_index, window);
|
||||
|
||||
var tracking_window = false;
|
||||
db.exec (
|
||||
"SELECT last_x, last_y, last_width, last_height FROM apps WHERE app_id = '%s' AND window_index = '%d';".printf (app_id, window_index),
|
||||
(n_columns, values, column_names) => {
|
||||
window.move_resize_frame (false, int.parse (values[0]), int.parse (values[1]), int.parse (values[2]), int.parse (values[3]));
|
||||
track_window (window, app_id);
|
||||
tracking_window = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
);
|
||||
|
||||
if (tracking_window) {
|
||||
// App was added in callback
|
||||
return;
|
||||
}
|
||||
|
||||
var frame_rect = window.get_frame_rect ();
|
||||
|
||||
Sqlite.Statement stmt;
|
||||
var rc = db.prepare_v2 (
|
||||
"INSERT INTO apps (app_id, window_index, last_x, last_y, last_width, last_height) VALUES ('%s', '%d', '%d', '%d', '%d', '%d');"
|
||||
.printf (app_id, window_index, frame_rect.x, frame_rect.y, frame_rect.width, frame_rect.height),
|
||||
-1, out stmt
|
||||
);
|
||||
|
||||
if (rc == Sqlite.OK) {
|
||||
rc = stmt.step ();
|
||||
if (rc == Sqlite.DONE) {
|
||||
track_window (window, app_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
critical ("Cannot insert app information into database: %d, %s", rc, db.errmsg ());
|
||||
}
|
||||
|
||||
private static void track_window (Meta.Window window, string app_id) {
|
||||
window.unmanaging.connect (on_window_unmanaging);
|
||||
}
|
||||
|
||||
private static void on_window_unmanaging (Meta.Window window) {
|
||||
var app_id = GLib.Markup.escape_text (window_tracker.get_app_for_window (window).id);
|
||||
|
||||
var window_index = find_window_index (window, app_id);
|
||||
app_windows[app_id].remove_index (window_index);
|
||||
var value = null; // insert_val requires lvalue
|
||||
app_windows[app_id].insert_val (window_index, value);
|
||||
|
||||
var frame_rect = window.get_frame_rect ();
|
||||
|
||||
Sqlite.Statement stmt;
|
||||
var rc = db.prepare_v2 (
|
||||
"UPDATE apps SET last_x = '%d', last_y = '%d', last_width = '%d', last_height = '%d' WHERE app_id = '%s' AND window_index = '%d';"
|
||||
.printf (frame_rect.x, frame_rect.y, frame_rect.width, frame_rect.height, app_id, window_index),
|
||||
-1, out stmt
|
||||
);
|
||||
|
||||
if (rc == Sqlite.OK) {
|
||||
rc = stmt.step ();
|
||||
if (rc == Sqlite.DONE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
critical ("Cannot update app position in database: %d, %s", rc, db.errmsg ());
|
||||
}
|
||||
|
||||
private static int find_window_index (Meta.Window window, string app_id) requires (app_id in app_windows) {
|
||||
unowned var windows_list = app_windows[app_id];
|
||||
var first_null = -1;
|
||||
for (int i = 0; i < windows_list.length; i++) {
|
||||
var w = windows_list.data[i];
|
||||
if (w == window) {
|
||||
return i;
|
||||
}
|
||||
|
||||
if (w == null && first_null == -1) {
|
||||
first_null = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_null != -1) {
|
||||
return first_null;
|
||||
}
|
||||
|
||||
return (int) windows_list.length;
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ gala_bin_sources = files(
|
||||
'SessionManager.vala',
|
||||
'WindowListener.vala',
|
||||
'WindowManager.vala',
|
||||
'WindowStateSaver.vala',
|
||||
'WindowTracker.vala',
|
||||
'WorkspaceManager.vala',
|
||||
'Zoom.vala',
|
||||
|
Loading…
Reference in New Issue
Block a user