windows_gui 2.0.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.
@@ -0,0 +1,99 @@
1
+ require 'windows_gui'
2
+
3
+ include WindowsGUI
4
+
5
+ def OnCreate(hwnd,
6
+ cs
7
+ )
8
+ SetWindowRgn(hwnd,
9
+ CreateEllipticRgn(*DPIAwareXY(42, 42, 420, 210)),
10
+ 0
11
+ )
12
+
13
+ 0
14
+ end
15
+
16
+ def OnDestroy(hwnd)
17
+ PostQuitMessage(0); 0
18
+ end
19
+
20
+ WindowProc = FFI::Function.new(:long,
21
+ [:pointer, :uint, :uint, :long],
22
+ convention: :stdcall
23
+ ) { |hwnd, uMsg, wParam, lParam|
24
+ begin
25
+ result = case uMsg
26
+ when WM_CREATE
27
+ OnCreate(hwnd, CREATESTRUCT.new(FFI::Pointer.new(lParam)))
28
+ when WM_DESTROY
29
+ OnDestroy(hwnd)
30
+ end
31
+
32
+ result || DefWindowProc(hwnd, uMsg, wParam, lParam)
33
+ rescue SystemExit => ex
34
+ PostQuitMessage(ex.status)
35
+ rescue
36
+ case MessageBox(hwnd,
37
+ L(Util.FormatException($!)),
38
+ APPNAME,
39
+ MB_ABORTRETRYIGNORE | MB_ICONERROR
40
+ )
41
+ when IDABORT
42
+ PostQuitMessage(2)
43
+ when IDRETRY
44
+ retry
45
+ end
46
+ end
47
+ }
48
+
49
+ def WinMain
50
+ WNDCLASSEX.new { |wc|
51
+ wc[:cbSize] = wc.size
52
+ wc[:lpfnWndProc] = WindowProc
53
+ wc[:hInstance] = GetModuleHandle(nil)
54
+ wc[:hIcon] = LoadIcon(nil, IDI_APPLICATION)
55
+ wc[:hCursor] = LoadCursor(nil, IDC_ARROW)
56
+ wc[:hbrBackground] = FFI::Pointer.new(COLOR_APPWORKSPACE + 1)
57
+
58
+ PWSTR(APPNAME) { |className|
59
+ wc[:lpszClassName] = className
60
+
61
+ DetonateLastError(0, :RegisterClassEx,
62
+ wc
63
+ )
64
+ }
65
+ }
66
+
67
+ hwnd = CreateWindowEx(
68
+ 0, APPNAME, APPNAME, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
69
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
70
+ nil, nil, GetModuleHandle(nil), nil
71
+ )
72
+
73
+ raise "CreateWindowEx failed (last error: #{GetLastError()})" if
74
+ hwnd.null? && GetLastError() != 0
75
+
76
+ exit(0) if hwnd.null?
77
+
78
+ ShowWindow(hwnd, SW_SHOWNORMAL)
79
+ UpdateWindow(hwnd)
80
+
81
+ MSG.new { |msg|
82
+ until DetonateLastError(-1, :GetMessage,
83
+ msg, nil, 0, 0
84
+ ) == 0
85
+ TranslateMessage(msg)
86
+ DispatchMessage(msg)
87
+ end
88
+
89
+ exit(msg[:wParam])
90
+ }
91
+ rescue
92
+ MessageBox(hwnd,
93
+ L(Util.FormatException($!)),
94
+ APPNAME,
95
+ MB_ICONERROR
96
+ ); exit(1)
97
+ end
98
+
99
+ WinMain()
@@ -0,0 +1,220 @@
1
+ require 'windows_gui'
2
+
3
+ include WindowsGUI
4
+
5
+ WndExtra = Struct.new(
6
+ :hpen,
7
+ :curpos,
8
+ :scribbles
9
+ )
10
+
11
+ def OnCreate(hwnd,
12
+ cs
13
+ )
14
+ xtra = Util::Id2Ref[GetWindowLong(hwnd, GWL_USERDATA)]
15
+
16
+ LOGPEN.new { |lp|
17
+ lp[:lopnWidth][:x] = DPIAwareX(10)
18
+ lp[:lopnColor] = RGB(255, 0, 0)
19
+
20
+ xtra[:hpen] = CreatePenIndirect(lp)
21
+ }
22
+
23
+ xtra[:scribbles] = []
24
+
25
+ 0
26
+ end
27
+
28
+ def OnDestroy(hwnd)
29
+ xtra = Util::Id2Ref[GetWindowLong(hwnd, GWL_USERDATA)]
30
+
31
+ DeleteObject(xtra[:hpen])
32
+
33
+ PostQuitMessage(0); 0
34
+ end
35
+
36
+ def OnPaint(hwnd,
37
+ ps
38
+ )
39
+ xtra = Util::Id2Ref[GetWindowLong(hwnd, GWL_USERDATA)]
40
+
41
+ UseObjects(ps[:hdc], xtra[:hpen]) {
42
+ xtra[:scribbles].each { |scribble|
43
+ MoveToEx(ps[:hdc], *scribble[0], nil)
44
+
45
+ scribble.each { |x, y|
46
+ LineTo(ps[:hdc], x, y)
47
+ }
48
+ }
49
+ }
50
+
51
+ 0
52
+ end
53
+
54
+ def OnLButtonDown(hwnd,
55
+ x, y
56
+ )
57
+ SetCapture(hwnd)
58
+
59
+ xtra = Util::Id2Ref[GetWindowLong(hwnd, GWL_USERDATA)]
60
+
61
+ xtra[:curpos] = [x, y]
62
+ xtra[:scribbles] << [[x, y]]
63
+
64
+ RECT.new { |rect|
65
+ SetRect(rect, x, y, x, y)
66
+ InflateRect(rect, *DPIAwareXY(5, 5))
67
+ InvalidateRect(hwnd, rect, 1)
68
+ }
69
+
70
+ 0
71
+ end
72
+
73
+ def OnLButtonUp(hwnd,
74
+ x, y
75
+ )
76
+ ReleaseCapture()
77
+
78
+ 0
79
+ end
80
+
81
+ def OnMouseMove(hwnd,
82
+ x, y
83
+ )
84
+ return 0 if GetCapture() != hwnd
85
+
86
+ xtra = Util::Id2Ref[GetWindowLong(hwnd, GWL_USERDATA)]
87
+
88
+ xtra[:scribbles].last << [x, y]
89
+
90
+ UseDC(hwnd) { |hdc|
91
+ UseObjects(hdc, xtra[:hpen]) {
92
+ MoveToEx(hdc, *xtra[:curpos], nil)
93
+ LineTo(hdc, x, y)
94
+
95
+ xtra[:curpos] = [x, y]
96
+ }
97
+ }
98
+
99
+ 0
100
+ end
101
+
102
+ def OnRButtonDown(hwnd,
103
+ x, y
104
+ )
105
+ return 0 if GetCapture() == hwnd
106
+
107
+ xtra = Util::Id2Ref[GetWindowLong(hwnd, GWL_USERDATA)]
108
+
109
+ xtra[:scribbles].clear
110
+
111
+ InvalidateRect(hwnd, nil, 1)
112
+
113
+ 0
114
+ end
115
+
116
+ WindowProc = FFI::Function.new(:long,
117
+ [:pointer, :uint, :uint, :long],
118
+ convention: :stdcall
119
+ ) { |hwnd, uMsg, wParam, lParam|
120
+ begin
121
+ result = case uMsg
122
+ when WM_NCCREATE
123
+ DefWindowProc(hwnd, uMsg, wParam, lParam)
124
+
125
+ SetWindowLong(hwnd,
126
+ GWL_USERDATA,
127
+ CREATESTRUCT.new(FFI::Pointer.new(lParam))[:lpCreateParams].to_i
128
+ )
129
+
130
+ 1
131
+ when WM_CREATE
132
+ OnCreate(hwnd, CREATESTRUCT.new(FFI::Pointer.new(lParam)))
133
+ when WM_DESTROY
134
+ OnDestroy(hwnd)
135
+
136
+ when WM_PAINT
137
+ DoPaint(hwnd) { |ps| result = OnPaint(hwnd, ps) }
138
+
139
+ when WM_LBUTTONDOWN
140
+ OnLButtonDown(hwnd, LOSHORT(lParam), HISHORT(lParam))
141
+ when WM_LBUTTONUP
142
+ OnLButtonUp(hwnd, LOSHORT(lParam), HISHORT(lParam))
143
+ when WM_MOUSEMOVE
144
+ OnMouseMove(hwnd, LOSHORT(lParam), HISHORT(lParam))
145
+
146
+ when WM_RBUTTONDOWN
147
+ OnRButtonDown(hwnd, LOSHORT(lParam), HISHORT(lParam))
148
+ end
149
+
150
+ result || DefWindowProc(hwnd, uMsg, wParam, lParam)
151
+ rescue SystemExit => ex
152
+ PostQuitMessage(ex.status)
153
+ rescue
154
+ case MessageBox(hwnd,
155
+ L(Util.FormatException($!)),
156
+ APPNAME,
157
+ MB_ABORTRETRYIGNORE | MB_ICONERROR
158
+ )
159
+ when IDABORT
160
+ PostQuitMessage(2)
161
+ when IDRETRY
162
+ retry
163
+ end
164
+ end
165
+ }
166
+
167
+ def WinMain
168
+ Util.Id2RefTrack(xtra = WndExtra.new)
169
+
170
+ WNDCLASSEX.new { |wc|
171
+ wc[:cbSize] = wc.size
172
+ wc[:lpfnWndProc] = WindowProc
173
+ wc[:cbWndExtra] = FFI::Type::Builtin::POINTER.size
174
+ wc[:hInstance] = GetModuleHandle(nil)
175
+ wc[:hIcon] = LoadIcon(nil, IDI_APPLICATION)
176
+ wc[:hCursor] = LoadCursor(nil, IDC_CROSS)
177
+ wc[:hbrBackground] = FFI::Pointer.new(COLOR_WINDOW + 1)
178
+
179
+ PWSTR(APPNAME) { |className|
180
+ wc[:lpszClassName] = className
181
+
182
+ DetonateLastError(0, :RegisterClassEx,
183
+ wc
184
+ )
185
+ }
186
+ }
187
+
188
+ hwnd = CreateWindowEx(
189
+ WS_EX_CLIENTEDGE, APPNAME, APPNAME, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
190
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
191
+ nil, nil, GetModuleHandle(nil), FFI::Pointer.new(xtra.object_id)
192
+ )
193
+
194
+ raise "CreateWindowEx failed (last error: #{GetLastError()})" if
195
+ hwnd.null? && GetLastError() != 0
196
+
197
+ exit(0) if hwnd.null?
198
+
199
+ ShowWindow(hwnd, SW_SHOWNORMAL)
200
+ UpdateWindow(hwnd)
201
+
202
+ MSG.new { |msg|
203
+ until DetonateLastError(-1, :GetMessage,
204
+ msg, nil, 0, 0
205
+ ) == 0
206
+ TranslateMessage(msg)
207
+ DispatchMessage(msg)
208
+ end
209
+
210
+ exit(msg[:wParam])
211
+ }
212
+ rescue
213
+ MessageBox(hwnd,
214
+ L(Util.FormatException($!)),
215
+ APPNAME,
216
+ MB_ICONERROR
217
+ ); exit(1)
218
+ end
219
+
220
+ WinMain()
@@ -0,0 +1,122 @@
1
+ require 'windows_gui'
2
+
3
+ include WindowsGUI
4
+
5
+ WndExtra = Struct.new(
6
+ :foo
7
+ )
8
+
9
+ def OnCreate(hwnd,
10
+ cs
11
+ )
12
+ xtra = Util::Id2Ref[GetWindowLong(hwnd, GWL_USERDATA)]
13
+
14
+ xtra[:foo] = L('Foo')
15
+
16
+ -1
17
+ end
18
+
19
+ def OnDestroy(hwnd)
20
+ xtra = Util::Id2Ref[GetWindowLong(hwnd, GWL_USERDATA)]
21
+
22
+ MessageBox(nil,
23
+ xtra[:foo],
24
+ APPNAME,
25
+ MB_ICONINFORMATION
26
+ )
27
+
28
+ PostQuitMessage(0); 0
29
+ end
30
+
31
+ WindowProc = FFI::Function.new(:long,
32
+ [:pointer, :uint, :uint, :long],
33
+ convention: :stdcall
34
+ ) { |hwnd, uMsg, wParam, lParam|
35
+ begin
36
+ result = case uMsg
37
+ when WM_NCCREATE
38
+ DefWindowProc(hwnd, uMsg, wParam, lParam)
39
+
40
+ SetWindowLong(hwnd,
41
+ GWL_USERDATA,
42
+ CREATESTRUCT.new(FFI::Pointer.new(lParam))[:lpCreateParams].to_i
43
+ )
44
+
45
+ 1
46
+ when WM_CREATE
47
+ OnCreate(hwnd, CREATESTRUCT.new(FFI::Pointer.new(lParam)))
48
+ when WM_DESTROY
49
+ OnDestroy(hwnd)
50
+ end
51
+
52
+ result || DefWindowProc(hwnd, uMsg, wParam, lParam)
53
+ rescue SystemExit => ex
54
+ PostQuitMessage(ex.status)
55
+ rescue
56
+ case MessageBox(hwnd,
57
+ L(Util.FormatException($!)),
58
+ APPNAME,
59
+ MB_ABORTRETRYIGNORE | MB_ICONERROR
60
+ )
61
+ when IDABORT
62
+ PostQuitMessage(2)
63
+ when IDRETRY
64
+ retry
65
+ end
66
+ end
67
+ }
68
+
69
+ def WinMain
70
+ Util.Id2RefTrack(xtra = WndExtra.new)
71
+
72
+ WNDCLASSEX.new { |wc|
73
+ wc[:cbSize] = wc.size
74
+ wc[:lpfnWndProc] = WindowProc
75
+ wc[:cbWndExtra] = FFI::Type::Builtin::POINTER.size
76
+ wc[:hInstance] = GetModuleHandle(nil)
77
+ wc[:hIcon] = LoadIcon(nil, IDI_APPLICATION)
78
+ wc[:hCursor] = LoadCursor(nil, IDC_ARROW)
79
+ wc[:hbrBackground] = FFI::Pointer.new(COLOR_WINDOW + 1)
80
+
81
+ PWSTR(APPNAME) { |className|
82
+ wc[:lpszClassName] = className
83
+
84
+ DetonateLastError(0, :RegisterClassEx,
85
+ wc
86
+ )
87
+ }
88
+ }
89
+
90
+ hwnd = CreateWindowEx(
91
+ 0, APPNAME, APPNAME, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
92
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
93
+ nil, nil, GetModuleHandle(nil), FFI::Pointer.new(xtra.object_id)
94
+ )
95
+
96
+ raise "CreateWindowEx failed (last error: #{GetLastError()})" if
97
+ hwnd.null? && GetLastError() != 0
98
+
99
+ exit(0) if hwnd.null?
100
+
101
+ ShowWindow(hwnd, SW_SHOWNORMAL)
102
+ UpdateWindow(hwnd)
103
+
104
+ MSG.new { |msg|
105
+ until DetonateLastError(-1, :GetMessage,
106
+ msg, nil, 0, 0
107
+ ) == 0
108
+ TranslateMessage(msg)
109
+ DispatchMessage(msg)
110
+ end
111
+
112
+ exit(msg[:wParam])
113
+ }
114
+ rescue
115
+ MessageBox(hwnd,
116
+ L(Util.FormatException($!)),
117
+ APPNAME,
118
+ MB_ICONERROR
119
+ ); exit(1)
120
+ end
121
+
122
+ WinMain()
Binary file
@@ -0,0 +1,152 @@
1
+ require 'weakref'
2
+ require 'ffi'
3
+
4
+ WINDOWS_GUI_VISUAL_STYLES = true unless defined?(WINDOWS_GUI_VISUAL_STYLES)
5
+ WINDOWS_GUI_DPI_AWARE = true unless defined?(WINDOWS_GUI_DPI_AWARE)
6
+
7
+ module WindowsGUI
8
+ extend FFI::Library
9
+
10
+ VERSION = '2.0.0'
11
+
12
+ module Util
13
+ def FormatException(ex)
14
+ str, trace = ex.to_s, ex.backtrace
15
+
16
+ str << "\n\n-- backtrace --\n\n" << trace.join("\n") if trace
17
+
18
+ str
19
+ end
20
+
21
+ module_function :FormatException
22
+
23
+ Id2Ref = {}
24
+
25
+ def Id2RefTrack(object)
26
+ Id2Ref[object.object_id] = WeakRef.new(object)
27
+
28
+ p "Object id #{object.object_id} of #{object} stored in Id2Ref track hash" if $DEBUG
29
+
30
+ ObjectSpace.define_finalizer(object, -> id {
31
+ Id2Ref.delete(id)
32
+
33
+ p "Object id #{id} deleted from Id2Ref track hash" if $DEBUG
34
+ })
35
+ end
36
+
37
+ module_function :Id2RefTrack
38
+
39
+ module ScopedStruct
40
+ def new(*args)
41
+ raise ArgumentError, 'Cannot accept both arguments and a block' if
42
+ args.length > 0 && block_given?
43
+
44
+ struct = super
45
+
46
+ return struct unless block_given?
47
+
48
+ begin
49
+ yield struct
50
+ ensure
51
+ struct.pointer.free
52
+
53
+ p "Native memory for #{struct} freed" if $DEBUG
54
+ end
55
+
56
+ nil
57
+ end
58
+ end
59
+ end
60
+
61
+ INVALID_HANDLE_VALUE = FFI::Pointer.new(-1)
62
+
63
+ def MAKEWORD(lobyte, hibyte)
64
+ (lobyte & 0xff) | ((hibyte & 0xff) << 8)
65
+ end
66
+
67
+ def LOBYTE(word)
68
+ word & 0xff
69
+ end
70
+
71
+ def HIBYTE(word)
72
+ (word >> 8) & 0xff
73
+ end
74
+
75
+ module_function :MAKEWORD, :LOBYTE, :HIBYTE
76
+
77
+ def MAKELONG(loword, hiword)
78
+ (loword & 0xffff) | ((hiword & 0xffff) << 16)
79
+ end
80
+
81
+ def LOWORD(long)
82
+ long & 0xffff
83
+ end
84
+
85
+ def HIWORD(long)
86
+ (long >> 16) & 0xffff
87
+ end
88
+
89
+ def LOSHORT(long)
90
+ ((loshort = LOWORD(long)) > 0x7fff) ? loshort - 0x1_0000 : loshort
91
+ end
92
+
93
+ def HISHORT(long)
94
+ ((hishort = HIWORD(long)) > 0x7fff) ? hishort - 0x1_0000 : hishort
95
+ end
96
+
97
+ module_function :MAKELONG, :LOWORD, :HIWORD, :LOSHORT, :HISHORT
98
+
99
+ def L(str)
100
+ (str << "\0").encode!('utf-16le')
101
+ end
102
+
103
+ def PWSTR(wstr)
104
+ raise 'Invalid Unicode string' unless
105
+ wstr.encoding == Encoding::UTF_16LE && wstr[-1] == L('')
106
+
107
+ ptr = FFI::MemoryPointer.new(:ushort, wstr.length).
108
+ put_bytes(0, wstr)
109
+
110
+ return ptr unless block_given?
111
+
112
+ begin
113
+ yield ptr
114
+ ensure
115
+ ptr.free
116
+
117
+ p "Native copy of '#{wstr[0...-1].encode($0.encoding)}' freed" if $DEBUG
118
+ end
119
+
120
+ nil
121
+ end
122
+
123
+ module_function :L, :PWSTR
124
+
125
+ APPNAME = L(File.basename($0, '.rbw'))
126
+
127
+ class POINT < FFI::Struct
128
+ extend Util::ScopedStruct
129
+
130
+ layout \
131
+ :x, :long,
132
+ :y, :long
133
+ end
134
+
135
+ class SIZE < FFI::Struct
136
+ extend Util::ScopedStruct
137
+
138
+ layout \
139
+ :cx, :long,
140
+ :cy, :long
141
+ end
142
+
143
+ class RECT < FFI::Struct
144
+ extend Util::ScopedStruct
145
+
146
+ layout \
147
+ :left, :long,
148
+ :top, :long,
149
+ :right, :long,
150
+ :bottom, :long
151
+ end
152
+ end