wmctile 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/wmctile +5 -0
- data/lib/wmctile/class.rb +13 -0
- data/lib/wmctile/class_with_dmenu.rb +24 -0
- data/lib/wmctile/memory.rb +64 -0
- data/lib/wmctile/router.rb +187 -0
- data/lib/wmctile/settings.rb +56 -0
- data/lib/wmctile/window.rb +69 -0
- data/lib/wmctile/window_manager.rb +174 -0
- data/lib/wmctile/window_tiler.rb +95 -0
- metadata +10 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4216479b45b21cb886179229cc5575b55b3fe1f
|
4
|
+
data.tar.gz: fb6b1145637252d54ba0620ed27ee10bc94dd206
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58ecfd20728f51d591d0b13d241046acae6bf1047ec37b9feb9158ae9275299a6506cfb1b76d9e6785d5be361a43328caed81a31e1eee8b6d6596048c4f77691
|
7
|
+
data.tar.gz: 5d981b8f5400b7fb1762e69797d33e764135a6052074db3073c96e7acd12626fa7ecfb988152a13ed0e8e5ce0959c85c9568fdb5d3cda9b5be98e047c19d0009
|
data/bin/wmctile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
class Wmctile::Class
|
2
|
+
def cmd cmd
|
3
|
+
# [0..-2] to strip the last \n
|
4
|
+
`#{ cmd }`[0..-2]
|
5
|
+
end
|
6
|
+
def notify title, string, icon = nil
|
7
|
+
if icon
|
8
|
+
system "notify-send -i '#{ icon }' '#{title}' '#{string}'"
|
9
|
+
else
|
10
|
+
system "notify-send '#{title}' '#{string}'"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'dmenu'
|
2
|
+
|
3
|
+
class Wmctile::ClassWithDmenu < Wmctile::Class
|
4
|
+
def dmenu items
|
5
|
+
a = Dmenu.new
|
6
|
+
# override defaults
|
7
|
+
a.background = '#242424'
|
8
|
+
a.case_insensitive = true
|
9
|
+
a.font = 'Ubuntu Mono-12'
|
10
|
+
a.foreground = 'white'
|
11
|
+
a.lines = 10
|
12
|
+
a.position = :bottom
|
13
|
+
a.selected_background = '#2e557e'
|
14
|
+
# set items
|
15
|
+
a.items = items
|
16
|
+
# run
|
17
|
+
b = a.run()
|
18
|
+
if b.is_a? Dmenu::Item
|
19
|
+
return b.value
|
20
|
+
else
|
21
|
+
return b
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
class Wmctile::Memory < Wmctile::Class
|
4
|
+
##################################
|
5
|
+
## init ##########################
|
6
|
+
##################################
|
7
|
+
def initialize
|
8
|
+
@path = File.expand_path '~/.config/wmctile/wmctile-memory.yml'
|
9
|
+
if not File.exist? @path
|
10
|
+
raw_memory = self.create_new_memory
|
11
|
+
end
|
12
|
+
raw_memory ||= File.read @path
|
13
|
+
@memory = YAML.load(raw_memory)
|
14
|
+
end
|
15
|
+
def create_new_memory
|
16
|
+
dir_path = @path[/(.*)\/wmctile-memory.yml/, 1]
|
17
|
+
if not Dir.exists? dir_path
|
18
|
+
Dir.mkdir dir_path
|
19
|
+
end
|
20
|
+
out_file = File.new @path, 'w'
|
21
|
+
# 20 workspaces should suffice
|
22
|
+
20.times do |i|
|
23
|
+
out_file.puts "#{i}:"
|
24
|
+
end
|
25
|
+
out_file.close
|
26
|
+
end
|
27
|
+
def write_memory
|
28
|
+
out_file = File.new @path, 'w'
|
29
|
+
out_file.puts @memory.to_yaml
|
30
|
+
out_file.close
|
31
|
+
end
|
32
|
+
##################################
|
33
|
+
## getters/setters ###############
|
34
|
+
##################################
|
35
|
+
def get workspace = 0, key = nil, key_sub = nil
|
36
|
+
begin
|
37
|
+
a = @memory[workspace]
|
38
|
+
if key.nil?
|
39
|
+
return nil
|
40
|
+
else
|
41
|
+
a = a[key]
|
42
|
+
if key_sub.nil?
|
43
|
+
return a
|
44
|
+
else
|
45
|
+
return a[key_sub]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
rescue Exception => e
|
49
|
+
return nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
def set workspace = 0, key, hash
|
53
|
+
hash.merge! 'time' => Time.now.to_i
|
54
|
+
if @memory[workspace].nil?
|
55
|
+
@memory[workspace] = {}
|
56
|
+
end
|
57
|
+
if @memory[workspace][key]
|
58
|
+
@memory[workspace][key] = hash
|
59
|
+
else
|
60
|
+
@memory[workspace].merge! key => hash
|
61
|
+
end
|
62
|
+
self.write_memory
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
class Wmctile::Router < Wmctile::Class
|
2
|
+
##################################
|
3
|
+
## init ##########################
|
4
|
+
##################################
|
5
|
+
def initialize
|
6
|
+
@settings = Wmctile::Settings.new
|
7
|
+
@all_workspaces = false
|
8
|
+
end
|
9
|
+
##################################
|
10
|
+
## main dispatch method ##########
|
11
|
+
##################################
|
12
|
+
def dispatch args = []
|
13
|
+
if args.length
|
14
|
+
main_arg = args[0]
|
15
|
+
if ['--all-workspaces', '-a'].include? main_arg
|
16
|
+
@all_workspaces = true
|
17
|
+
drop = 2
|
18
|
+
main_arg = args[1]
|
19
|
+
else
|
20
|
+
@all_workspaces = false
|
21
|
+
drop = 1
|
22
|
+
end
|
23
|
+
if main_arg and !['dispatch', 'initialize', 'wm', 'wt', 'memory'].include? main_arg and self.respond_to? main_arg
|
24
|
+
self.send main_arg, *args.drop(drop)
|
25
|
+
else
|
26
|
+
self.help
|
27
|
+
end
|
28
|
+
else
|
29
|
+
self.help
|
30
|
+
end
|
31
|
+
end
|
32
|
+
##################################
|
33
|
+
## object getter methods #########
|
34
|
+
##################################
|
35
|
+
def wm
|
36
|
+
@wm || @wm = Wmctile::WindowManager.new(@settings)
|
37
|
+
end
|
38
|
+
def wt
|
39
|
+
# @wm might be nil
|
40
|
+
@wt || @wt = Wmctile::WindowTiler.new(@settings, self.memory, @wm)
|
41
|
+
end
|
42
|
+
def memory
|
43
|
+
@memory || @memory = Wmctile::Memory.new
|
44
|
+
end
|
45
|
+
##################################
|
46
|
+
## actual command-line methods ###
|
47
|
+
##################################
|
48
|
+
def help args = nil
|
49
|
+
puts <<-eos
|
50
|
+
wmctile version 0.1.1
|
51
|
+
|
52
|
+
usage:
|
53
|
+
wmctile [--option1, --option2, ...] <command> ['argument1', 'argument2', ...]
|
54
|
+
|
55
|
+
examples:
|
56
|
+
wmctile snap 'left' 'terminator'
|
57
|
+
wmctile summon --all-workspaces ':ACTIVE:'
|
58
|
+
|
59
|
+
options:
|
60
|
+
--all-workspaces, -a
|
61
|
+
Use all workspaces when searching for windows.
|
62
|
+
|
63
|
+
commands:
|
64
|
+
summon 'window_string'
|
65
|
+
Summons a window matching 'window_str'.
|
66
|
+
|
67
|
+
summon_or_run 'window_string' 'command_to_run'
|
68
|
+
Summons a window matching 'window_string'. If no window is found, the 'command_to_run' is run.
|
69
|
+
|
70
|
+
switch_to 'window_string'
|
71
|
+
Switches to a window matching 'window_string'.
|
72
|
+
|
73
|
+
switch_to_or_run 'window_string' 'command_to_run'
|
74
|
+
Switches to a window matching 'window_string'. If no window is found, the 'command_to_run' is run.
|
75
|
+
|
76
|
+
maximize 'window_string'
|
77
|
+
Maximizes a window matching 'window_string'.
|
78
|
+
|
79
|
+
unmaximize 'window_string'
|
80
|
+
Unmaximizes a window matching 'window_string'.
|
81
|
+
|
82
|
+
shade 'window_string'
|
83
|
+
Shades a window matching 'window_string'.
|
84
|
+
|
85
|
+
unshade 'window_string'
|
86
|
+
Unshades a window matching 'window_string'.
|
87
|
+
|
88
|
+
unshade_last_shaded
|
89
|
+
Unshades the last shaded window on active workspace.
|
90
|
+
|
91
|
+
snap 'where' 'window_string' ['portion']
|
92
|
+
Snaps a window matching 'window_string' to occupy the 'where' 'portion' of the screen.
|
93
|
+
'where' can be one of 'left', 'right', 'top', 'bottom'
|
94
|
+
'portion' is a float number with the default of 0.5
|
95
|
+
|
96
|
+
resize 'where' ['portion']
|
97
|
+
Resizes the last performed action (snap/tile etc.) on active workspace.
|
98
|
+
'where' can be one of 'left', 'right', 'top', 'bottom'
|
99
|
+
The action depends on the previously performed action. When you resize 'left' a previous snap 'left', you're shrinking the window. When you resize 'left' a previous snap 'right', you're increasing the size of the window.
|
100
|
+
'portion' is a float number with the default of 0.01 by which to edit the previous portion of the screen
|
101
|
+
|
102
|
+
resize_snap 'where' ['portion']
|
103
|
+
Resizes the last performed snap on active workspace. Arguments are the same as in resize command.
|
104
|
+
|
105
|
+
additional information:
|
106
|
+
To use the active window, pass ':ACTIVE:' as the 'window_string' argument.
|
107
|
+
eos
|
108
|
+
end
|
109
|
+
def summon window_str
|
110
|
+
window = self.wm.find_in_windows window_str, @all_workspaces
|
111
|
+
if window
|
112
|
+
window.summon
|
113
|
+
return true
|
114
|
+
else
|
115
|
+
return false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
def summon_or_run window_str, cmd_to_run
|
119
|
+
unless self.summon window_str
|
120
|
+
self.cmd "#{ cmd_to_run } > /dev/null &"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
def switch_to window_str
|
124
|
+
window = self.wm.find_in_windows window_str, @all_workspaces
|
125
|
+
if window
|
126
|
+
window.switch_to
|
127
|
+
return true
|
128
|
+
else
|
129
|
+
return false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
def switch_to_or_run window_str, cmd_to_run
|
133
|
+
unless self.switch_to window_str
|
134
|
+
self.cmd "#{ cmd_to_run } > /dev/null &"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
def maximize window_str
|
138
|
+
window = self.wm.get_window window_str
|
139
|
+
if window
|
140
|
+
window.maximize
|
141
|
+
end
|
142
|
+
end
|
143
|
+
def unmaximize window_str
|
144
|
+
window = self.wm.get_window window_str
|
145
|
+
if window
|
146
|
+
window.unmaximize
|
147
|
+
end
|
148
|
+
end
|
149
|
+
def shade window_str
|
150
|
+
window = self.wm.get_window window_str
|
151
|
+
if window
|
152
|
+
window.shade
|
153
|
+
self.memory.set self.wm.workspace, 'shade', {
|
154
|
+
'window_id' => window.id
|
155
|
+
}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
def unshade window_str
|
159
|
+
window = self.wm.get_window window_str
|
160
|
+
if window
|
161
|
+
window.unshade
|
162
|
+
self.memory.set self.wm.workspace, 'unshade', {
|
163
|
+
'window_id' => window.id
|
164
|
+
}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
def unshade_last_shaded
|
168
|
+
win_id = self.memory.get self.wm.workspace, 'shade', 'window_id'
|
169
|
+
window = Wmctile::Window.new win_id, @settings
|
170
|
+
if window
|
171
|
+
window.unshade
|
172
|
+
self.memory.set self.wm.workspace, 'unshade', {
|
173
|
+
'window_id' => window.id
|
174
|
+
}
|
175
|
+
end
|
176
|
+
end
|
177
|
+
def snap where = 'left', window_str = nil, portion = 0.5
|
178
|
+
self.wt.snap where, window_str, portion
|
179
|
+
end
|
180
|
+
def resize where = 'left', portion = 0.01
|
181
|
+
self.wt.resize where, portion
|
182
|
+
end
|
183
|
+
def resize_snap where = 'left', portion = 0.01
|
184
|
+
self.wt.resize_snap where, portion
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
class Wmctile::Settings < Wmctile::Class
|
4
|
+
def method_missing sym, *args, &block
|
5
|
+
return false
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
path = File.expand_path '~/.config/wmctile/wmctile-settings.yml'
|
10
|
+
if not File.exist? path
|
11
|
+
raw_settings = self.create_new_settings path
|
12
|
+
end
|
13
|
+
raw_settings ||= File.read path
|
14
|
+
settings = YAML.load(raw_settings)
|
15
|
+
if settings
|
16
|
+
settings.each { |name, value|
|
17
|
+
instance_variable_set("@#{name}", value)
|
18
|
+
self.class.class_eval { attr_reader name.intern }
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_requirements
|
24
|
+
req = ['xrandr', 'wmctrl', 'dmenu']
|
25
|
+
ret = req.reject { |r| self.cmd("which #{ r }").length > 0 }
|
26
|
+
return ret
|
27
|
+
end
|
28
|
+
def create_new_settings path
|
29
|
+
req = self.test_requirements
|
30
|
+
unless req.nil? or req.length == 0
|
31
|
+
puts <<-eos
|
32
|
+
You don't have #{ req.join(', ') } installed. Wmctile can't run without that.
|
33
|
+
|
34
|
+
To fix this on Ubuntu, run:
|
35
|
+
|
36
|
+
sudo apt-get install #{ req.join(' ') }
|
37
|
+
eos
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
dir_path = path[/(.*)\/wmctile-settings.yml/, 1]
|
41
|
+
if not Dir.exists? dir_path
|
42
|
+
Dir.mkdir dir_path
|
43
|
+
end
|
44
|
+
out_file = File.new path, 'w'
|
45
|
+
out_file.puts self.default_settings.to_yaml
|
46
|
+
out_file.close
|
47
|
+
end
|
48
|
+
def default_settings
|
49
|
+
{
|
50
|
+
:window_border => 1,
|
51
|
+
:panel_height => 24,
|
52
|
+
:panel_width => 0,
|
53
|
+
:hostname => self.cmd('hostname')
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class Wmctile::Window < Wmctile::Class
|
2
|
+
attr_accessor :id, :name, :title
|
3
|
+
|
4
|
+
def initialize win_string, settings
|
5
|
+
@default_movement = { :x => 0, :y => 0, :width => '-1', :height => '-1' }
|
6
|
+
@settings = settings
|
7
|
+
@id = win_string[/0x[\d\w]{8}/]
|
8
|
+
self.get_name_and_title win_string
|
9
|
+
end
|
10
|
+
def get_name_and_title win_string
|
11
|
+
if win_string == @id
|
12
|
+
@name = ''
|
13
|
+
@title = ''
|
14
|
+
else
|
15
|
+
after_id_and_workspace = win_string[14..-1].split(/\s+#{ @settings.hostname }\s+/, 2)
|
16
|
+
@name = after_id_and_workspace[0]
|
17
|
+
@title = after_id_and_workspace[1]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
def dmenu_item
|
21
|
+
unless @dmenu_item
|
22
|
+
str = "#{ @id } #{ @name } #{ @title }"
|
23
|
+
@dmenu_item = Dmenu::Item.new str, self
|
24
|
+
end
|
25
|
+
@dmenu_item
|
26
|
+
end
|
27
|
+
def get_name
|
28
|
+
if @name == ''
|
29
|
+
self.get_name_and_title self.cmd('wmctrl -lx | grep ' + @id)
|
30
|
+
end
|
31
|
+
@name
|
32
|
+
end
|
33
|
+
def get_name_length
|
34
|
+
@name.length
|
35
|
+
end
|
36
|
+
def set_name_length name_length
|
37
|
+
@name += ' '*(name_length - @name.length)
|
38
|
+
end
|
39
|
+
def wmctrl wm_cmd = '', summon = false
|
40
|
+
self.cmd "wmctrl -i#{ summon ? 'R' : 'r' } #{ @id } #{ wm_cmd }"
|
41
|
+
return self # return self so that commands can be chained
|
42
|
+
end
|
43
|
+
def move how_to_move = {}
|
44
|
+
how_to_move = @default_movement.merge! how_to_move
|
45
|
+
cmd = "-e 0,#{ how_to_move[:x].to_i },#{ how_to_move[:y].to_i },#{ how_to_move[:width].to_i },#{ how_to_move[:height].to_i }"
|
46
|
+
self.unshade
|
47
|
+
self.unmaximize
|
48
|
+
self.wmctrl cmd
|
49
|
+
end
|
50
|
+
def shade
|
51
|
+
self.wmctrl '-b add,shaded'
|
52
|
+
end
|
53
|
+
def unshade
|
54
|
+
self.wmctrl '-b remove,shaded'
|
55
|
+
end
|
56
|
+
def summon
|
57
|
+
self.wmctrl '', true
|
58
|
+
end
|
59
|
+
def switch_to
|
60
|
+
self.cmd "wmctrl -ia #{ @id }"
|
61
|
+
return self
|
62
|
+
end
|
63
|
+
def maximize
|
64
|
+
self.wmctrl '-b add,maximized_vert,maximized_horz'
|
65
|
+
end
|
66
|
+
def unmaximize
|
67
|
+
self.wmctrl '-b remove,maximized_vert,maximized_horz'
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
class Wmctile::WindowManager < Wmctile::ClassWithDmenu
|
2
|
+
attr_accessor :w, :h, :workspace, :windows
|
3
|
+
##################################
|
4
|
+
## init ##########################
|
5
|
+
##################################
|
6
|
+
def initialize settings
|
7
|
+
@settings = settings
|
8
|
+
self.init_dimensions
|
9
|
+
end
|
10
|
+
def init_dimensions
|
11
|
+
# legacy (but fast) method via wmctrl
|
12
|
+
# dimensions = cmd("wmctrl -d | awk '{ print $9 }' | head -n1").split('x')
|
13
|
+
# xrandr is slower, but offers more information
|
14
|
+
dimensions = cmd("xrandr | grep -E '\sconnected\s[0-9]+x[0-9]+\+0' | awk '{print $3}' | awk -F'+' '{print $1}'").split('x')
|
15
|
+
@w = dimensions[0].to_i - 2*@settings.window_border
|
16
|
+
@h = dimensions[1].to_i - 2*@settings.window_border - @settings.panel_height
|
17
|
+
@workspace = cmd("wmctrl -d | grep '\*' | awk '{ print $1 }'").to_i
|
18
|
+
end
|
19
|
+
##################################
|
20
|
+
## dimension getters #############
|
21
|
+
##################################
|
22
|
+
def width portion = 1
|
23
|
+
@w * portion
|
24
|
+
end
|
25
|
+
def height portion = 1
|
26
|
+
@h * portion
|
27
|
+
end
|
28
|
+
##################################
|
29
|
+
## window getters ################
|
30
|
+
##################################
|
31
|
+
def get_window window_str = nil, all_workspaces = false
|
32
|
+
if window_str.nil?
|
33
|
+
window = self.ask_for_window all_workspaces
|
34
|
+
else
|
35
|
+
if window_str == ':ACTIVE:'
|
36
|
+
window = self.get_active_window
|
37
|
+
else
|
38
|
+
window = self.find_window window_str, all_workspaces
|
39
|
+
unless window
|
40
|
+
# does window_str have an icon bundle in it? (aka evince.Evince)
|
41
|
+
if window_str =~ /[a-z0-9]+\.[a-z0-9]+/i
|
42
|
+
icon = window_str.split('.').first
|
43
|
+
else
|
44
|
+
icon = nil
|
45
|
+
end
|
46
|
+
self.notify 'No window found', "#{ window_str }", icon
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
window
|
51
|
+
end
|
52
|
+
def get_active_window
|
53
|
+
win_id = self.cmd('wmctrl -a :ACTIVE: -v 2>&1').split('Using window: ').last
|
54
|
+
Wmctile::Window.new win_id, @settings
|
55
|
+
end
|
56
|
+
def find_window window_string, all_workspaces = false
|
57
|
+
cmd = "wmctrl -lx | grep -F #{ window_string }"
|
58
|
+
cmd += ' | grep -E \'0x\w+\s+' + @workspace.to_s + '\s+\'' unless all_workspaces
|
59
|
+
window_string = self.cmd cmd
|
60
|
+
window_string = window_string.split("\n").first
|
61
|
+
if window_string.nil?
|
62
|
+
return nil
|
63
|
+
else
|
64
|
+
return Wmctile::Window.new window_string, @settings
|
65
|
+
end
|
66
|
+
end
|
67
|
+
def find_windows window_string, all_workspaces = false
|
68
|
+
cmd = "wmctrl -lx | grep -F #{ window_string }"
|
69
|
+
cmd += ' | grep -E \'0x\w+\s+' + @workspace.to_s + '\s+\'' unless all_workspaces
|
70
|
+
window_strings = self.cmd cmd
|
71
|
+
window_strings = window_strings.split("\n")
|
72
|
+
if window_string.nil?
|
73
|
+
return nil
|
74
|
+
else
|
75
|
+
return window_strings.map { |w| Wmctile::Window.new w, @settings }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
def find_in_windows window_string, all_workspaces = false
|
79
|
+
if window_string.nil?
|
80
|
+
self.ask_for_window all_workspaces
|
81
|
+
else
|
82
|
+
windows = self.find_windows window_string, all_workspaces
|
83
|
+
if windows
|
84
|
+
ids = windows.collect(&:id)
|
85
|
+
active_win = self.get_active_window
|
86
|
+
if ids.include? active_win.id
|
87
|
+
# cycle through the windows
|
88
|
+
i = ids.index active_win.id
|
89
|
+
# try the next one
|
90
|
+
if ids[i+1]
|
91
|
+
window = windows[i+1]
|
92
|
+
# fallback to the first one
|
93
|
+
else
|
94
|
+
window = windows.first
|
95
|
+
end
|
96
|
+
else
|
97
|
+
# switch to the first one
|
98
|
+
window = windows.first
|
99
|
+
end
|
100
|
+
return window
|
101
|
+
end
|
102
|
+
return nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
def ask_for_window all_workspaces = false
|
106
|
+
self.dmenu self.windows.map(&:dmenu_item)
|
107
|
+
end
|
108
|
+
##################################
|
109
|
+
## window lists ##################
|
110
|
+
##################################
|
111
|
+
def build_win_list all_workspaces = false
|
112
|
+
unless all_workspaces
|
113
|
+
variable_name = '@windows_on_workspace'
|
114
|
+
else
|
115
|
+
variable_name = '@windows_all'
|
116
|
+
end
|
117
|
+
unless instance_variable_get(variable_name)
|
118
|
+
unless all_workspaces
|
119
|
+
cmd = "wmctrl -lx | grep \" #{ @workspace } \""
|
120
|
+
else
|
121
|
+
cmd = "wmctrl -lx"
|
122
|
+
end
|
123
|
+
arr = cmd(cmd).split("\n")
|
124
|
+
|
125
|
+
new_list = arr.map { |w| Wmctile::Window.new(w, @settings) }
|
126
|
+
name_length = new_list.map(&:get_name_length).max
|
127
|
+
new_list.each { |w| w.set_name_length(name_length) }
|
128
|
+
|
129
|
+
instance_variable_set(variable_name, new_list)
|
130
|
+
end
|
131
|
+
instance_variable_get(variable_name)
|
132
|
+
end
|
133
|
+
def windows all_workspaces = false
|
134
|
+
unless all_workspaces
|
135
|
+
variable_name = '@windows_on_workspace'
|
136
|
+
else
|
137
|
+
variable_name = '@windows_all'
|
138
|
+
end
|
139
|
+
unless instance_variable_get(variable_name)
|
140
|
+
self.build_win_list all_workspaces
|
141
|
+
else
|
142
|
+
instance_variable_get(variable_name)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
##################################
|
146
|
+
## window size calculators #######
|
147
|
+
##################################
|
148
|
+
def calculate_snap where, portion = 0.5
|
149
|
+
return case where
|
150
|
+
when 'left'
|
151
|
+
{
|
152
|
+
:x => @settings.panel_width, :y => @settings.panel_height,
|
153
|
+
:height => self.height, :width => self.width(portion)
|
154
|
+
}
|
155
|
+
when 'right'
|
156
|
+
{
|
157
|
+
:x => self.width(portion), :y => @settings.panel_height,
|
158
|
+
:height => self.height, :width => self.width(1-portion)
|
159
|
+
}
|
160
|
+
when 'top'
|
161
|
+
{
|
162
|
+
:x => @settings.panel_width, :y => @settings.panel_height,
|
163
|
+
:height => self.height(portion), :width => self.width
|
164
|
+
}
|
165
|
+
when 'bottom'
|
166
|
+
{
|
167
|
+
:x => @settings.panel_width, :y => @settings.panel_height + self.height(1-portion),
|
168
|
+
:height => self.height(portion), :width => self.width
|
169
|
+
}
|
170
|
+
else
|
171
|
+
nil
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
class Wmctile::WindowTiler < Wmctile::Class
|
2
|
+
##################################
|
3
|
+
## init ##########################
|
4
|
+
##################################
|
5
|
+
def initialize settings, memory, wm = nil
|
6
|
+
@settings = settings
|
7
|
+
@memory = memory
|
8
|
+
@wm = wm
|
9
|
+
end
|
10
|
+
##################################
|
11
|
+
## object getter methods #########
|
12
|
+
##################################
|
13
|
+
def wm
|
14
|
+
@wm || @wm = Wmctile::WindowManager.new(@settings)
|
15
|
+
end
|
16
|
+
def memory
|
17
|
+
@memory || @memory = Wmctile::Memory.new
|
18
|
+
end
|
19
|
+
##################################
|
20
|
+
## actual snapping methods #######
|
21
|
+
##################################
|
22
|
+
def snap where = 'left', window_str = nil, portion = 0.5
|
23
|
+
window = self.wm.get_window window_str
|
24
|
+
if window
|
25
|
+
how_to_move = self.wm.calculate_snap where, portion.to_f
|
26
|
+
if how_to_move
|
27
|
+
window.move how_to_move
|
28
|
+
self.memory.set self.wm.workspace, 'snap', {
|
29
|
+
'where' => where, 'portion' => portion, 'window_id' => window.id
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
def resize where = 'left', portion = 0.01
|
35
|
+
portion = portion.to_f
|
36
|
+
# what are we moving? the last one used from these:
|
37
|
+
methods = ['snap']
|
38
|
+
freshest_meth = nil
|
39
|
+
freshest_time = 0
|
40
|
+
methods.each do |meth|
|
41
|
+
time = self.memory.get self.wm.workspace, meth, 'time'
|
42
|
+
if time > freshest_time
|
43
|
+
freshest_time = time
|
44
|
+
freshest_meth = meth
|
45
|
+
end
|
46
|
+
end
|
47
|
+
# ok, got it, it's freshest_meth
|
48
|
+
self.send "resize_#{ freshest_meth }", where, portion
|
49
|
+
end
|
50
|
+
def resize_snap where = 'left', portion = 0.01
|
51
|
+
portion = portion.to_f
|
52
|
+
info = self.memory.get self.wm.workspace, 'snap'
|
53
|
+
if info.nil?
|
54
|
+
return nil
|
55
|
+
end
|
56
|
+
negative = case info['where']
|
57
|
+
when 'left'
|
58
|
+
if where == 'left'
|
59
|
+
true
|
60
|
+
elsif where == 'right'
|
61
|
+
false
|
62
|
+
else
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
when 'right'
|
66
|
+
if where == 'left'
|
67
|
+
false
|
68
|
+
elsif where == 'right'
|
69
|
+
true
|
70
|
+
else
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
when 'top'
|
74
|
+
if where == 'bottom'
|
75
|
+
false
|
76
|
+
elsif where == 'top'
|
77
|
+
true
|
78
|
+
else
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
when 'bottom'
|
82
|
+
if where == 'top'
|
83
|
+
false
|
84
|
+
elsif where == 'bottom'
|
85
|
+
true
|
86
|
+
else
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
unless negative.nil?
|
91
|
+
portion = -portion if negative
|
92
|
+
self.snap info['where'], info['window_id'], info['portion']+portion
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wmctile
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- mreq
|
@@ -30,7 +30,16 @@ executables: []
|
|
30
30
|
extensions: []
|
31
31
|
extra_rdoc_files: []
|
32
32
|
files:
|
33
|
+
- bin/wmctile
|
33
34
|
- lib/wmctile.rb
|
35
|
+
- lib/wmctile/class.rb
|
36
|
+
- lib/wmctile/class_with_dmenu.rb
|
37
|
+
- lib/wmctile/memory.rb
|
38
|
+
- lib/wmctile/router.rb
|
39
|
+
- lib/wmctile/settings.rb
|
40
|
+
- lib/wmctile/window.rb
|
41
|
+
- lib/wmctile/window_manager.rb
|
42
|
+
- lib/wmctile/window_tiler.rb
|
34
43
|
homepage: http://mreq.github.io/wmctile
|
35
44
|
licenses:
|
36
45
|
- GPL-2
|