wmadd 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/wmadd.rb ADDED
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'wmadd/version'
4
+ require_relative 'wmadd/x'
5
+ require 'tmtms/timer'
6
+ require 'timeout'
7
+
8
+ # Wmadd
9
+ class Wmadd
10
+ DEFAULT_OPACITY = 0xFFFFFFFF
11
+ OVERWRAP_OPACITY = 0x80000000
12
+
13
+ def self.start
14
+ self.new.start
15
+ end
16
+
17
+ def initialize
18
+ @display = X::Display.new
19
+ @root = @display.default_root_window
20
+ @timer = Tmtms::Timer.new
21
+ end
22
+
23
+ def start
24
+ window_list.each do |w|
25
+ watch_resize_event(w)
26
+ end
27
+
28
+ @root.select_input(X::SUBSTRUCTURE_NOTIFY_MASK | X::PROPERTY_CHANGE_MASK)
29
+ @osd = @root.create_simple_window(100, 100, 200, 50, 0, 0, 0xe0e0e0)
30
+ @osd.change_property_atom(:_NET_WM_WINDOW_TYPE, X::PROP_MODE_REPLACE, :_NET_WM_WINDOW_TYPE_NOTIFICATION)
31
+ @osd.select_input(X::EXPOSURE_MASK)
32
+ @gc = @osd.create_gc
33
+ @gc.set_background(0xe0e0e0)
34
+ @gc.set_foreground(0)
35
+ @gc.set_font(@display.load_font("fixed"))
36
+
37
+ while true
38
+ ev = @display.next_event
39
+ case ev
40
+ when X::CreateWindowEvent
41
+ window_list.each do |w|
42
+ watch_resize_event(w)
43
+ end
44
+ when X::ExposeEvent
45
+ display_osd if ev.window == @osd.id
46
+ when X::PropertyEvent
47
+ if ev.atom == @display.intern_atom(:_NET_ACTIVE_WINDOW)
48
+ reset_opacity
49
+ end
50
+ when X::ConfigureEvent
51
+ lazy_reset_opacity
52
+ win = X::Window.new(@display, ev.window)
53
+ display_osd(win)
54
+ end
55
+ end
56
+ end
57
+
58
+ def display_osd(win=nil)
59
+ if win
60
+ wm_win = wm_window(win) or return
61
+ hints = wm_win.wm_normal_hints or return
62
+ top_win = top_window(win) or return
63
+
64
+ x, y = top_win.geometry.then{[_1.x, _1.y]}
65
+ width, height = win.geometry.then{[_1.width, _1.height]}
66
+
67
+ @osd_x = x + ((width - 200) / 2)
68
+ @osd_y = y + ((height - 50) / 2)
69
+ @osd_str = "#{width} x #{height} +#{x}+#{y}"
70
+ if hints.width_inc > 0 && hints.height_inc > 0
71
+ width -= hints.base_width
72
+ height -= hints.base_height
73
+ @osd_str += " (#{width / hints.width_inc} x #{height / hints.height_inc})"
74
+ end
75
+ end
76
+
77
+ @osd.map_window
78
+ @osd.clear
79
+ @gc.draw_string(10, 30, @osd_str)
80
+ @osd.move_window(@osd_x, @osd_y)
81
+ @display.flush
82
+ @job&.cancel
83
+ @job = @timer.set(1) do
84
+ @osd.unmap_window
85
+ @display.flush
86
+ end
87
+ end
88
+
89
+ def lazy_reset_opacity
90
+ @reset_opacity_job&.cancel
91
+ @reset_opacity_job = @timer.set(0.1){reset_opacity}
92
+ end
93
+
94
+ def top_window(win)
95
+ top = win
96
+ while top.parent.id != @root.id
97
+ top = top.parent
98
+ end
99
+ top
100
+ end
101
+
102
+ def wm_window(win)
103
+ while win && win.parent&.id != @root.id
104
+ if win.window_attributes.map_state == X::IS_VIEWABLE && win.window_type == :normal
105
+ return win
106
+ end
107
+ win = win.parent
108
+ end
109
+ nil
110
+ end
111
+
112
+ def reset_opacity
113
+ return unless @root.active_window
114
+ active_win = wm_window(@root.active_window) or return
115
+
116
+ f = false
117
+ window_list.each do |w|
118
+ if active_win.id == w.id
119
+ f = true
120
+ set_default_opacity(w)
121
+ next
122
+ end
123
+ if f && overwrap?(w, active_win)
124
+ set_overwrap_opacity(w)
125
+ next
126
+ end
127
+ set_default_opacity(w)
128
+ end
129
+ end
130
+
131
+ def overwrap?(w1, w2)
132
+ g1 = top_window(w1).geometry
133
+ g2 = top_window(w2).geometry
134
+ g1.x < g2.x+g2.width && g1.x+g1.width > g2.x && g1.y < g2.y+g2.height && g1.y+g1.height > g2.y
135
+ end
136
+
137
+ def set_default_opacity(win)
138
+ win.change_property_cardinal(:_NET_WM_WINDOW_OPACITY, X::PROP_MODE_REPLACE, DEFAULT_OPACITY)
139
+ end
140
+
141
+ def set_overwrap_opacity(win)
142
+ win.change_property_cardinal(:_NET_WM_WINDOW_OPACITY, X::PROP_MODE_REPLACE, OVERWRAP_OPACITY)
143
+ end
144
+
145
+ def window_list(win=@root)
146
+ list = []
147
+ win.query_tree&.each do |w|
148
+ if w.window_attributes.map_state == X::IS_VIEWABLE && w.window_type == :normal
149
+ list.push w
150
+ else
151
+ list.push window_list(w)
152
+ end
153
+ end
154
+ list.flatten
155
+ end
156
+
157
+ def watch_resize_event(win)
158
+ win.select_input(X::STRUCTURE_NOTIFY_MASK)
159
+ end
160
+ end
@@ -0,0 +1,4 @@
1
+ module Wmadd
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wmadd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - TOMITA Masahiro
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2024-10-06 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: tmtms_timer
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ description: show window geometry
27
+ email:
28
+ - tommy@tmtm.org
29
+ executables:
30
+ - wmadd
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".rspec"
35
+ - LICENSE.txt
36
+ - README.md
37
+ - Rakefile
38
+ - exe/wmadd
39
+ - lib/wmadd.rb
40
+ - lib/wmadd/version.rb
41
+ - lib/wmadd/x.rb
42
+ - sig/window_geometry.rbs
43
+ homepage: https://gitlab.com/tmtms/wmadd
44
+ licenses:
45
+ - GPLv3
46
+ metadata:
47
+ homepage_uri: https://gitlab.com/tmtms/wmadd
48
+ source_code_uri: https://gitlab.com/tmtms/wmadd
49
+ rubygems_mfa_required: 'true'
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 3.3.0
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubygems_version: 3.6.0.dev
65
+ specification_version: 4
66
+ summary: show window geometry
67
+ test_files: []