2017-07-08 09:30:31 +03:00
|
|
|
//
|
|
|
|
// Copyright (C) 2017 Adam Bieńkowski
|
|
|
|
//
|
|
|
|
// 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/>.
|
|
|
|
//
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
public class Gala.Plugins.PIP.Plugin : Gala.Plugin {
|
2020-01-09 22:26:30 +03:00
|
|
|
private const int MIN_SELECTION_SIZE = 30;
|
|
|
|
|
|
|
|
private Gee.ArrayList<PopupWindow> windows;
|
|
|
|
private Gala.WindowManager? wm = null;
|
|
|
|
private SelectionArea? selection_area;
|
|
|
|
|
2023-02-18 20:32:32 +03:00
|
|
|
private static inline bool meta_rectangle_contains (Meta.Rectangle rect, int x, int y) {
|
2020-01-09 22:26:30 +03:00
|
|
|
return x >= rect.x && x < rect.x + rect.width
|
|
|
|
&& y >= rect.y && y < rect.y + rect.height;
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
construct {
|
2020-01-09 22:26:30 +03:00
|
|
|
windows = new Gee.ArrayList<PopupWindow> ();
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
public override void initialize (Gala.WindowManager wm) {
|
2020-01-09 22:26:30 +03:00
|
|
|
this.wm = wm;
|
|
|
|
var display = wm.get_display ();
|
|
|
|
var settings = new GLib.Settings (Config.SCHEMA + ".keybindings");
|
2017-07-08 09:30:31 +03:00
|
|
|
|
2023-03-25 23:17:12 +03:00
|
|
|
display.add_keybinding ("pip", settings, Meta.KeyBindingFlags.IGNORE_AUTOREPEAT, (Meta.KeyHandlerFunc) on_initiate);
|
2020-01-09 22:26:30 +03:00
|
|
|
}
|
2017-07-08 09:30:31 +03:00
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
public override void destroy () {
|
2020-01-09 22:26:30 +03:00
|
|
|
clear_selection_area ();
|
2017-07-08 09:30:31 +03:00
|
|
|
|
2020-01-09 22:26:30 +03:00
|
|
|
foreach (var popup_window in windows) {
|
|
|
|
untrack_window (popup_window);
|
|
|
|
}
|
2017-07-08 09:30:31 +03:00
|
|
|
|
2020-01-09 22:26:30 +03:00
|
|
|
windows.clear ();
|
|
|
|
}
|
2017-07-08 09:30:31 +03:00
|
|
|
|
2020-01-09 22:26:30 +03:00
|
|
|
[CCode (instance_pos = -1)]
|
2023-02-18 20:32:32 +03:00
|
|
|
private void on_initiate (Meta.Display display, Meta.Window? window, Clutter.KeyEvent event,
|
2020-01-15 03:44:21 +03:00
|
|
|
Meta.KeyBinding binding) {
|
2020-01-09 22:26:30 +03:00
|
|
|
selection_area = new SelectionArea (wm);
|
|
|
|
selection_area.selected.connect (on_selection_actor_selected);
|
|
|
|
selection_area.captured.connect (on_selection_actor_captured);
|
|
|
|
selection_area.closed.connect (clear_selection_area);
|
|
|
|
|
|
|
|
track_actor (selection_area);
|
|
|
|
wm.ui_group.add_child (selection_area);
|
|
|
|
|
|
|
|
selection_area.start_selection ();
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
private void on_selection_actor_selected (int x, int y) {
|
2020-01-09 22:26:30 +03:00
|
|
|
clear_selection_area ();
|
|
|
|
select_window_at (x, y);
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
private void on_selection_actor_captured (int x, int y, int width, int height) {
|
2020-01-09 22:26:30 +03:00
|
|
|
clear_selection_area ();
|
|
|
|
|
|
|
|
if (width < MIN_SELECTION_SIZE || height < MIN_SELECTION_SIZE) {
|
|
|
|
select_window_at (x, y);
|
|
|
|
} else {
|
|
|
|
var active = get_active_window_actor ();
|
|
|
|
if (active != null) {
|
|
|
|
int point_x = x - (int)active.x;
|
|
|
|
int point_y = y - (int)active.y;
|
|
|
|
|
2023-02-19 06:41:31 +03:00
|
|
|
// Compensate for server-side window decorations
|
|
|
|
var input_rect = active.get_meta_window ().get_buffer_rect ();
|
|
|
|
var outer_rect = active.get_meta_window ().get_frame_rect ();
|
|
|
|
point_x -= outer_rect.x - input_rect.x;
|
|
|
|
point_y -= outer_rect.y - input_rect.y;
|
|
|
|
|
2020-03-13 01:38:28 +03:00
|
|
|
var rect = Graphene.Rect.alloc ();
|
2020-03-11 12:48:22 +03:00
|
|
|
rect.init (point_x, point_y, width, height);
|
2020-01-09 22:26:30 +03:00
|
|
|
|
2020-03-11 12:48:22 +03:00
|
|
|
var popup_window = new PopupWindow (wm, active);
|
|
|
|
popup_window.set_container_clip (rect);
|
2020-01-09 22:26:30 +03:00
|
|
|
popup_window.show.connect (on_popup_window_show);
|
|
|
|
popup_window.hide.connect (on_popup_window_hide);
|
|
|
|
add_window (popup_window);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
private void on_popup_window_show (Clutter.Actor popup_window) {
|
2020-01-09 22:26:30 +03:00
|
|
|
track_actor (popup_window);
|
|
|
|
update_region ();
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
private void on_popup_window_hide (Clutter.Actor popup_window) {
|
2020-01-09 22:26:30 +03:00
|
|
|
untrack_actor (popup_window);
|
|
|
|
update_region ();
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
private void select_window_at (int x, int y) {
|
2020-01-09 22:26:30 +03:00
|
|
|
var selected = get_window_actor_at (x, y);
|
|
|
|
if (selected != null) {
|
2020-03-11 12:48:22 +03:00
|
|
|
var popup_window = new PopupWindow (wm, selected);
|
2020-01-09 22:26:30 +03:00
|
|
|
popup_window.show.connect (on_popup_window_show);
|
|
|
|
popup_window.hide.connect (on_popup_window_hide);
|
|
|
|
add_window (popup_window);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
private void clear_selection_area () {
|
2020-01-09 22:26:30 +03:00
|
|
|
if (selection_area != null) {
|
|
|
|
untrack_actor (selection_area);
|
|
|
|
update_region ();
|
|
|
|
|
|
|
|
selection_area.destroy ();
|
|
|
|
selection_area = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
private Meta.WindowActor? get_window_actor_at (int x, int y) {
|
2020-01-09 22:26:30 +03:00
|
|
|
unowned Meta.Display display = wm.get_display ();
|
|
|
|
unowned List<Meta.WindowActor> actors = display.get_window_actors ();
|
2017-07-08 09:30:31 +03:00
|
|
|
|
2020-01-09 22:26:30 +03:00
|
|
|
var copy = actors.copy ();
|
|
|
|
copy.reverse ();
|
2017-07-08 09:30:31 +03:00
|
|
|
|
2020-01-09 22:26:30 +03:00
|
|
|
weak Meta.WindowActor? selected = null;
|
|
|
|
copy.@foreach ((actor) => {
|
|
|
|
if (selected != null) {
|
|
|
|
return;
|
|
|
|
}
|
2017-07-08 09:30:31 +03:00
|
|
|
|
2020-01-09 22:26:30 +03:00
|
|
|
var window = actor.get_meta_window ();
|
|
|
|
var rect = window.get_frame_rect ();
|
2018-10-04 01:23:10 +03:00
|
|
|
|
2020-01-09 22:26:30 +03:00
|
|
|
if (!actor.is_destroyed () && !window.is_hidden () && !window.is_skip_taskbar () && meta_rectangle_contains (rect, x, y)) {
|
|
|
|
selected = actor;
|
|
|
|
}
|
|
|
|
});
|
2017-07-08 09:30:31 +03:00
|
|
|
|
2020-01-09 22:26:30 +03:00
|
|
|
return selected;
|
|
|
|
}
|
2017-07-08 09:30:31 +03:00
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
private Meta.WindowActor? get_active_window_actor () {
|
2020-01-09 22:26:30 +03:00
|
|
|
unowned Meta.Display display = wm.get_display ();
|
|
|
|
unowned List<Meta.WindowActor> actors = display.get_window_actors ();
|
2017-07-08 09:30:31 +03:00
|
|
|
|
2020-01-09 22:26:30 +03:00
|
|
|
var copy = actors.copy ();
|
|
|
|
copy.reverse ();
|
|
|
|
|
|
|
|
weak Meta.WindowActor? active = null;
|
|
|
|
actors.@foreach ((actor) => {
|
|
|
|
if (active != null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var window = actor.get_meta_window ();
|
|
|
|
if (!actor.is_destroyed () && !window.is_hidden () && !window.is_skip_taskbar () && window.has_focus ()) {
|
|
|
|
active = actor;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return active;
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
private void add_window (PopupWindow popup_window) {
|
2020-01-09 22:26:30 +03:00
|
|
|
popup_window.closed.connect (() => remove_window (popup_window));
|
|
|
|
windows.add (popup_window);
|
|
|
|
wm.ui_group.add_child (popup_window);
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
private void remove_window (PopupWindow popup_window) {
|
2020-01-09 22:26:30 +03:00
|
|
|
windows.remove (popup_window);
|
|
|
|
untrack_window (popup_window);
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
private void untrack_window (PopupWindow popup_window) {
|
2020-01-09 22:26:30 +03:00
|
|
|
untrack_actor (popup_window);
|
|
|
|
update_region ();
|
|
|
|
popup_window.destroy ();
|
|
|
|
}
|
2017-07-08 09:30:31 +03:00
|
|
|
}
|
|
|
|
|
2020-01-15 03:44:21 +03:00
|
|
|
public Gala.PluginInfo register_plugin () {
|
2020-01-09 22:26:30 +03:00
|
|
|
return Gala.PluginInfo () {
|
|
|
|
name = "Popup Window",
|
|
|
|
author = "Adam Bieńkowski <donadigos159@gmail.com>",
|
|
|
|
plugin_type = typeof (Gala.Plugins.PIP.Plugin),
|
|
|
|
provides = Gala.PluginFunction.ADDITION,
|
|
|
|
load_priority = Gala.LoadPriority.IMMEDIATE
|
|
|
|
};
|
2017-07-08 09:30:31 +03:00
|
|
|
}
|