ffi-wingui-core 0.2.0 → 0.3.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,246 @@
1
+ require 'ffi-wingui-core'
2
+
3
+ include WinGUI
4
+
5
+ EnableVisualStyles()
6
+
7
+ APPNAME = L(File.basename(__FILE__, '.rbw'))
8
+
9
+ WndExtra = Struct.new(
10
+ :scribbles,
11
+ :hpen
12
+ )
13
+
14
+ class WndExtra
15
+ REFS = {}
16
+
17
+ def initialize(*args)
18
+ super
19
+
20
+ REFS[object_id] = self
21
+
22
+ ObjectSpace.define_finalizer(self, -> id {
23
+ REFS.delete(id)
24
+ })
25
+ end
26
+ end
27
+
28
+ def onCreate(hwnd,
29
+ cs
30
+ )
31
+ xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
32
+
33
+ xtra[:scribbles] = []
34
+
35
+ LOGPEN.new { |lp|
36
+ lp[:lopnWidth][:x] = DPIAwareX(4)
37
+ lp[:lopnColor] = RGB(255, 0, 0)
38
+
39
+ xtra[:hpen] = CreatePenIndirect(lp)
40
+ }
41
+
42
+ 0
43
+ end
44
+
45
+ def onDestroy(hwnd)
46
+ xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
47
+
48
+ DeleteObject(xtra[:hpen])
49
+
50
+ PostQuitMessage(0); 0
51
+ end
52
+
53
+ def onPaint(hwnd,
54
+ ps
55
+ )
56
+ xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
57
+
58
+ hdefpen = SelectObject(ps[:hdc], xtra[:hpen])
59
+
60
+ xtra[:scribbles].each { |scribble|
61
+ MoveToEx(ps[:hdc], *scribble[0], nil)
62
+
63
+ scribble.each { |x, y|
64
+ LineTo(ps[:hdc], x, y)
65
+ }
66
+ }
67
+
68
+ 0
69
+ ensure
70
+ SelectObject(ps[:hdc], hdefpen)
71
+ end
72
+
73
+ def onLButtonDown(hwnd,
74
+ x, y
75
+ )
76
+ xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
77
+
78
+ SetCapture(hwnd)
79
+
80
+ (xtra[:scribbles] << []).last << [x, y]
81
+
82
+ RECT.new { |rect|
83
+ rect[:left] = x - DPIAwareX(2)
84
+ rect[:top] = y - DPIAwareY(2)
85
+ rect[:right] = x + DPIAwareX(2)
86
+ rect[:bottom] = y + DPIAwareY(2)
87
+
88
+ InvalidateRect(hwnd, rect, 1)
89
+ }
90
+
91
+ 0
92
+ end
93
+
94
+ def onLButtonUp(hwnd,
95
+ x, y
96
+ )
97
+ xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
98
+
99
+ ReleaseCapture()
100
+
101
+ 0
102
+ end
103
+
104
+ def onMouseMove(hwnd,
105
+ x, y
106
+ )
107
+ return 0 if GetCapture() != hwnd
108
+
109
+ xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
110
+
111
+ xtra[:scribbles].last << [x, y]
112
+
113
+ InvalidateRect(hwnd, nil, 0)
114
+
115
+ 0
116
+ end
117
+
118
+ def onRButtonDown(hwnd,
119
+ x, y
120
+ )
121
+ return 0 if GetCapture() == hwnd
122
+
123
+ xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
124
+
125
+ xtra[:scribbles].clear
126
+
127
+ InvalidateRect(hwnd, nil, 1)
128
+
129
+ 0
130
+ end
131
+
132
+ WindowProc = FFI::Function.new(:long,
133
+ [:pointer, :uint, :uint, :long],
134
+ convention: :stdcall
135
+ ) { |hwnd, uMsg, wParam, lParam|
136
+ begin
137
+ result = case uMsg
138
+ when WM_NCCREATE
139
+ DefWindowProc(hwnd, uMsg, wParam, lParam)
140
+
141
+ SetWindowLong(hwnd,
142
+ GWL_USERDATA,
143
+ CREATESTRUCT.new(FFI::Pointer.new(lParam))[:lpCreateParams].to_i
144
+ )
145
+
146
+ 1
147
+ when WM_CREATE
148
+ onCreate(hwnd, CREATESTRUCT.new(FFI::Pointer.new(lParam)))
149
+ when WM_DESTROY
150
+ onDestroy(hwnd)
151
+
152
+ when WM_PAINT
153
+ r = nil
154
+
155
+ if GetUpdateRect(hwnd, nil, 0) != 0
156
+ PAINTSTRUCT.new { |ps|
157
+ BeginPaint(hwnd, ps)
158
+
159
+ begin
160
+ r = onPaint(hwnd, ps)
161
+ ensure
162
+ EndPaint(hwnd, ps)
163
+ end
164
+ }
165
+ end
166
+
167
+ when WM_LBUTTONDOWN
168
+ onLButtonDown(hwnd, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))
169
+ when WM_LBUTTONUP
170
+ onLButtonUp(hwnd, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))
171
+ when WM_MOUSEMOVE
172
+ onMouseMove(hwnd, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))
173
+
174
+ when WM_RBUTTONDOWN
175
+ onRButtonDown(hwnd, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))
176
+ end
177
+
178
+ result || DefWindowProc(hwnd, uMsg, wParam, lParam)
179
+ rescue
180
+ case MessageBox(hwnd,
181
+ L(Util.FormatException($!)),
182
+ APPNAME,
183
+ MB_ABORTRETRYIGNORE | MB_ICONERROR
184
+ )
185
+ when IDABORT
186
+ PostQuitMessage(2)
187
+ when IDRETRY
188
+ retry
189
+ end
190
+ end
191
+ }
192
+
193
+ def main
194
+ xtra = WndExtra.new
195
+
196
+ WNDCLASSEX.new { |wc|
197
+ wc[:cbSize] = wc.size
198
+ wc[:lpfnWndProc] = WindowProc
199
+ wc[:cbWndExtra] = FFI::Type::Builtin::POINTER.size
200
+ wc[:hInstance] = GetModuleHandle(nil)
201
+ wc[:hIcon] = LoadIcon(nil, IDI_APPLICATION)
202
+ wc[:hCursor] = LoadCursor(nil, IDC_CROSS)
203
+ wc[:hbrBackground] = FFI::Pointer.new(COLOR_WINDOW + 1)
204
+
205
+ PWSTR(APPNAME) { |className|
206
+ wc[:lpszClassName] = className
207
+
208
+ DetonateLastError(0, :RegisterClassEx,
209
+ wc
210
+ )
211
+ }
212
+ }
213
+
214
+ hwnd = CreateWindowEx(
215
+ 0, APPNAME, APPNAME, WS_OVERLAPPEDWINDOW,
216
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
217
+ nil, nil, GetModuleHandle(nil), FFI::Pointer.new(xtra.object_id)
218
+ )
219
+
220
+ raise "CreateWindowEx failed (last error: #{GetLastError()})" if
221
+ hwnd.null? && GetLastError() != 0
222
+
223
+ exit(0) if hwnd.null?
224
+
225
+ ShowWindow(hwnd, SW_SHOWNORMAL)
226
+ UpdateWindow(hwnd)
227
+
228
+ MSG.new { |msg|
229
+ until DetonateLastError(-1, :GetMessage,
230
+ msg, nil, 0, 0
231
+ ) == 0
232
+ TranslateMessage(msg)
233
+ DispatchMessage(msg)
234
+ end
235
+
236
+ exit(msg[:wParam])
237
+ }
238
+ rescue
239
+ MessageBox(hwnd,
240
+ L(Util.FormatException($!)),
241
+ APPNAME,
242
+ MB_ICONERROR
243
+ ); exit(1)
244
+ end
245
+
246
+ main
@@ -8,26 +8,11 @@ APPNAME = L(File.basename(__FILE__, '.rbw'))
8
8
 
9
9
  WndExtra = Struct.new(
10
10
  :foo
11
- )
12
-
13
- class WndExtra
14
- REFS = {}
15
-
16
- def initialize(*args)
17
- super
18
-
19
- REFS[object_id] = self
20
-
21
- ObjectSpace.define_finalizer(self, -> id {
22
- REFS.delete(id)
23
- })
24
- end
25
- end
11
+ ).send(:include, Util::Id2RefTracking)
26
12
 
27
13
  def onCreate(hwnd,
28
14
  cs
29
15
  )
30
- #xtra = ObjectSpace._id2ref(GetWindowLong(hwnd, GWL_USERDATA))
31
16
  xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
32
17
 
33
18
  xtra[:foo] = L('Foo')
@@ -36,7 +21,6 @@ def onCreate(hwnd,
36
21
  end
37
22
 
38
23
  def onDestroy(hwnd)
39
- #xtra = ObjectSpace._id2ref(GetWindowLong(hwnd, GWL_USERDATA))
40
24
  xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
41
25
 
42
26
  MessageBox(nil,
@@ -71,31 +55,39 @@ begin
71
55
 
72
56
  result || DefWindowProc(hwnd, uMsg, wParam, lParam)
73
57
  rescue
74
- MessageBox(nil,
75
- L(%'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}'),
58
+ case MessageBox(hwnd,
59
+ L(Util.FormatException($!)),
76
60
  APPNAME,
77
- MB_ICONERROR
78
- ); PostQuitMessage(2)
61
+ MB_ABORTRETRYIGNORE | MB_ICONERROR
62
+ )
63
+ when IDABORT
64
+ PostQuitMessage(2)
65
+ when IDRETRY
66
+ retry
67
+ end
79
68
  end
80
69
  }
81
70
 
82
71
  def main
83
- wc = WNDCLASSEX.new
84
72
  xtra = WndExtra.new
85
73
 
86
- wc[:cbSize] = wc.size
87
- wc[:lpfnWndProc] = WindowProc
88
- #wc[:cbWndExtra] = FFI::Pointer.size
89
- wc[:cbWndExtra] = FFI::Type::Builtin::POINTER.size
90
- wc[:hInstance] = GetModuleHandle(nil)
91
- wc[:hIcon] = LoadIcon(nil, IDI_APPLICATION)
92
- wc[:hCursor] = LoadCursor(nil, IDC_ARROW)
93
- wc[:hbrBackground] = FFI::Pointer.new(COLOR_WINDOW + 1)
94
- wc[:lpszClassName] = className = PWSTR(APPNAME)
74
+ WNDCLASSEX.new { |wc|
75
+ wc[:cbSize] = wc.size
76
+ wc[:lpfnWndProc] = WindowProc
77
+ wc[:cbWndExtra] = FFI::Type::Builtin::POINTER.size
78
+ wc[:hInstance] = GetModuleHandle(nil)
79
+ wc[:hIcon] = LoadIcon(nil, IDI_APPLICATION)
80
+ wc[:hCursor] = LoadCursor(nil, IDC_ARROW)
81
+ wc[:hbrBackground] = FFI::Pointer.new(COLOR_WINDOW + 1)
82
+
83
+ PWSTR(APPNAME) { |className|
84
+ wc[:lpszClassName] = className
95
85
 
96
- DetonateLastError(0, :RegisterClassEx,
97
- wc
98
- ) { className.free }
86
+ DetonateLastError(0, :RegisterClassEx,
87
+ wc
88
+ )
89
+ }
90
+ }
99
91
 
100
92
  hwnd = CreateWindowEx(
101
93
  0, APPNAME, APPNAME, WS_OVERLAPPEDWINDOW,
@@ -106,22 +98,24 @@ def main
106
98
  raise "CreateWindowEx failed (last error: #{GetLastError()})" if
107
99
  hwnd.null? && GetLastError() != 0
108
100
 
101
+ exit(0) if hwnd.null?
102
+
109
103
  ShowWindow(hwnd, SW_SHOWNORMAL)
110
104
  UpdateWindow(hwnd)
111
105
 
112
- msg = MSG.new
113
-
114
- until DetonateLastError(-1, :GetMessage,
115
- msg, nil, 0, 0
116
- ) == 0
117
- TranslateMessage(msg)
118
- DispatchMessage(msg)
119
- end
106
+ MSG.new { |msg|
107
+ until DetonateLastError(-1, :GetMessage,
108
+ msg, nil, 0, 0
109
+ ) == 0
110
+ TranslateMessage(msg)
111
+ DispatchMessage(msg)
112
+ end
120
113
 
121
- exit(msg[:wParam])
114
+ exit(msg[:wParam])
115
+ }
122
116
  rescue
123
- MessageBox(nil,
124
- L(%'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}'),
117
+ MessageBox(hwnd,
118
+ L(Util.FormatException($!)),
125
119
  APPNAME,
126
120
  MB_ICONERROR
127
121
  ); exit(1)
@@ -3,51 +3,104 @@ require 'ffi'
3
3
  module WinGUI
4
4
  extend FFI::Library
5
5
 
6
+ #{ common
7
+ module Util
8
+ def FormatException(ex)
9
+ str, trace = ex.to_s, ex.backtrace
10
+
11
+ str << "\n\n-- backtrace --\n\n" << trace.join("\n") if trace
12
+
13
+ str
14
+ end
15
+
16
+ module_function :FormatException
17
+
18
+ module Id2RefTracking
19
+ REFS = {}
20
+
21
+ def initialize(*args)
22
+ super
23
+
24
+ REFS[object_id] = self
25
+
26
+ ObjectSpace.define_finalizer(self, -> id {
27
+ REFS.delete(id)
28
+ })
29
+ end
30
+ end
31
+
32
+ module ScopedStruct
33
+ def new(*args)
34
+ raise ArgumentError, 'Cannot accept both arguments and a block' if
35
+ args.length > 0 && block_given?
36
+
37
+ struct = super
38
+
39
+ return struct unless block_given?
40
+
41
+ begin
42
+ yield struct
43
+ ensure
44
+ struct.pointer.free
45
+
46
+ p "Native memory for #{struct} freed" if $DEBUG
47
+ end
48
+
49
+ nil
50
+ end
51
+ end
52
+ end
53
+
6
54
  INVALID_HANDLE_VALUE = FFI::Pointer.new(-1)
7
55
 
8
- def MAKEWORD(low, high)
9
- (low & 0xff) | ((high & 0xff) << 8)
56
+ def MAKEWORD(lobyte, hibyte)
57
+ (lobyte & 0xff) | ((hibyte & 0xff) << 8)
10
58
  end
11
59
 
12
- def LOBYTE(w)
13
- w & 0xff
60
+ def LOBYTE(word)
61
+ word & 0xff
14
62
  end
15
63
 
16
- def HIBYTE(w)
17
- (w >> 8) & 0xff
64
+ def HIBYTE(word)
65
+ (word >> 8) & 0xff
18
66
  end
19
67
 
20
- def MAKELONG(low, high)
21
- (low & 0xffff) | ((high & 0xffff) << 16)
68
+ def MAKELONG(loword, hiword)
69
+ (loword & 0xffff) | ((hiword & 0xffff) << 16)
22
70
  end
23
71
 
24
- def LOWORD(l)
25
- l & 0xffff
72
+ def LOWORD(long)
73
+ long & 0xffff
26
74
  end
27
75
 
28
- def HIWORD(l)
29
- (l >> 16) & 0xffff
76
+ def HIWORD(long)
77
+ (long >> 16) & 0xffff
30
78
  end
31
79
 
32
80
  module_function \
33
81
  :MAKEWORD, :LOBYTE, :HIBYTE,
34
82
  :MAKELONG, :LOWORD, :HIWORD
35
83
 
36
- def L(s)
37
- (s << "\0").encode!('utf-16le')
84
+ def L(str)
85
+ (str << "\0").encode!('utf-16le')
38
86
  end
39
87
 
40
- def PWSTR(ws)
88
+ def PWSTR(wstr)
41
89
  raise 'Invalid Unicode string' unless
42
- ws.encoding == Encoding::UTF_16LE &&
43
- ws[-1] == L('')
90
+ wstr.encoding == Encoding::UTF_16LE && wstr[-1] == L('')
91
+
92
+ ptr = FFI::MemoryPointer.new(:ushort, wstr.length).
93
+ put_bytes(0, wstr)
44
94
 
45
- p = FFI::MemoryPointer.new(:ushort, ws.length).
46
- put_bytes(0, ws)
95
+ return ptr unless block_given?
47
96
 
48
- return p unless block_given?
97
+ begin
98
+ yield ptr
99
+ ensure
100
+ ptr.free
49
101
 
50
- begin yield p ensure p.free end
102
+ p "Native copy of '#{wstr[0...-1].encode($0.encoding)}' freed" if $DEBUG
103
+ end
51
104
 
52
105
  nil
53
106
  end
@@ -55,55 +108,33 @@ module WinGUI
55
108
  module_function :L, :PWSTR
56
109
 
57
110
  class POINT < FFI::Struct
111
+ extend Util::ScopedStruct
112
+
58
113
  layout \
59
114
  :x, :long,
60
115
  :y, :long
61
-
62
- def self.[](x, y)
63
- new.tap { |point|
64
- point[:x], point[:y] = x, y
65
- }
66
- end
67
-
68
- def to_a
69
- [self[:x], self[:y]]
70
- end
71
116
  end
72
117
 
73
118
  class SIZE < FFI::Struct
119
+ extend Util::ScopedStruct
120
+
74
121
  layout \
75
122
  :cx, :long,
76
123
  :cy, :long
77
-
78
- def self.[](cx, cy)
79
- new.tap { |size|
80
- size[:cx], size[:cy] = cx, cy
81
- }
82
- end
83
-
84
- def to_a
85
- [self[:cx], self[:cy]]
86
- end
87
124
  end
88
125
 
89
126
  class RECT < FFI::Struct
127
+ extend Util::ScopedStruct
128
+
90
129
  layout \
91
130
  :left, :long,
92
131
  :top, :long,
93
132
  :right, :long,
94
133
  :bottom, :long
95
-
96
- def self.[](l, t, r, b)
97
- new.tap { |rect|
98
- rect[:left], rect[:top], rect[:right], rect[:bottom] = l, t, r, b
99
- }
100
- end
101
-
102
- def to_a
103
- [self[:left], self[:top], self[:right], self[:bottom]]
104
- end
105
134
  end
135
+ #}
106
136
 
137
+ #{ kernel32
107
138
  ffi_lib 'kernel32'
108
139
  ffi_convention :stdcall
109
140
 
@@ -136,6 +167,8 @@ module WinGUI
136
167
  module_function :Detonate, :DetonateLastError
137
168
 
138
169
  class OSVERSIONINFOEX < FFI::Struct
170
+ extend Util::ScopedStruct
171
+
139
172
  layout \
140
173
  :dwOSVersionInfoSize, :ulong,
141
174
  :dwMajorVersion, :ulong,
@@ -151,10 +184,12 @@ module WinGUI
151
184
  end
152
185
 
153
186
  attach_function :GetVersionEx, :GetVersionExW, [
154
- :pointer
187
+ :pointer # OSVERSIONINFOEX.by_ref
155
188
  ], :int
156
189
 
157
- VERSION = OSVERSIONINFOEX.new.tap { |ovi|
190
+ OSVERSION = OSVERSIONINFOEX.new.tap { |ovi|
191
+ at_exit { OSVERSION.pointer.free }
192
+
158
193
  ovi[:dwOSVersionInfoSize] = ovi.size
159
194
 
160
195
  DetonateLastError(0, :GetVersionEx,
@@ -201,8 +236,8 @@ module WinGUI
201
236
  #}
202
237
 
203
238
  NTDDI_VERSION = MAKELONG(
204
- MAKEWORD(VERSION[:wServicePackMinor], VERSION[:wServicePackMajor]),
205
- MAKEWORD(VERSION[:dwMinorVersion], VERSION[:dwMajorVersion])
239
+ MAKEWORD(OSVERSION[:wServicePackMinor], OSVERSION[:wServicePackMajor]),
240
+ MAKEWORD(OSVERSION[:dwMinorVersion], OSVERSION[:dwMajorVersion])
206
241
  )
207
242
 
208
243
  #{ WINxxx
@@ -214,6 +249,14 @@ module WinGUI
214
249
 
215
250
  WINVER = HIWORD(NTDDI_VERSION)
216
251
 
252
+ if WINVER == WIN2K
253
+ class << FFI::Struct
254
+ def by_ref(*args)
255
+ FFI::Type::Builtin::POINTER
256
+ end
257
+ end
258
+ end
259
+
217
260
  attach_function :GetModuleHandle, :GetModuleHandleW, [
218
261
  :buffer_in
219
262
  ], :pointer
@@ -228,6 +271,8 @@ module WinGUI
228
271
 
229
272
  if WINVER >= WINXP
230
273
  class ACTCTX < FFI::Struct
274
+ extend Util::ScopedStruct
275
+
231
276
  layout \
232
277
  :cbSize, :ulong,
233
278
  :dwFlags, :ulong,
@@ -241,7 +286,7 @@ module WinGUI
241
286
  end
242
287
 
243
288
  attach_function :CreateActCtx, :CreateActCtxW, [
244
- :pointer
289
+ ACTCTX.by_ref
245
290
  ], :pointer
246
291
 
247
292
  attach_function :ReleaseActCtx, [
@@ -281,7 +326,7 @@ module WinGUI
281
326
  raise 'Visual styles already enabled' if
282
327
  COMMON_CONTROLS_ACTCTX[:activated]
283
328
 
284
- manifest = "#{ENV['TEMP']}/Microsoft.Windows.Common-Controls.manifest"
329
+ manifest = "#{ENV['TEMP']}/WinGUI.Common-Controls.manifest"
285
330
 
286
331
  File.open(manifest, 'w:utf-8') { |file|
287
332
  file << <<-XML
@@ -303,15 +348,18 @@ module WinGUI
303
348
  XML
304
349
  }
305
350
 
306
- ac = ACTCTX.new
351
+ ACTCTX.new { |ac|
352
+ ac[:cbSize] = ac.size
307
353
 
308
- ac[:cbSize] = ac.size
309
- ac[:lpSource] = source = PWSTR(L(manifest))
354
+ PWSTR(L(manifest)) { |source|
355
+ ac[:lpSource] = source
310
356
 
311
- COMMON_CONTROLS_ACTCTX[:handle] =
312
- DetonateLastError(INVALID_HANDLE_VALUE, :CreateActCtx,
313
- ac
314
- ) { source.free }
357
+ COMMON_CONTROLS_ACTCTX[:handle] =
358
+ DetonateLastError(INVALID_HANDLE_VALUE, :CreateActCtx,
359
+ ac
360
+ )
361
+ }
362
+ }
315
363
 
316
364
  DetonateLastError(0, :ActivateActCtx,
317
365
  COMMON_CONTROLS_ACTCTX[:handle], COMMON_CONTROLS_ACTCTX[:cookie]
@@ -332,7 +380,9 @@ module WinGUI
332
380
  :int,
333
381
  :int
334
382
  ], :int
383
+ #}
335
384
 
385
+ #{ gdi32
336
386
  ffi_lib 'gdi32'
337
387
  ffi_convention :stdcall
338
388
 
@@ -352,18 +402,27 @@ module WinGUI
352
402
  LOBYTE(rgb >> 16)
353
403
  end
354
404
 
355
- module_function \
356
- :RGB, :GetRValue, :GetGValue, :GetBValue
405
+ module_function :RGB, :GetRValue, :GetGValue, :GetBValue
357
406
 
407
+ #{ GetDeviceCaps indices
358
408
  LOGPIXELSX = 88
359
409
  LOGPIXELSY = 90
410
+ #}
360
411
 
361
412
  attach_function :GetDeviceCaps, [
362
413
  :pointer,
363
414
  :int
364
415
  ], :int
365
416
 
417
+ def DPIAwareFontHeight(pointSize)
418
+ -MulDiv(pointSize, DPIY, 72)
419
+ end
420
+
421
+ module_function :DPIAwareFontHeight
422
+
366
423
  class LOGFONT < FFI::Struct
424
+ extend Util::ScopedStruct
425
+
367
426
  layout \
368
427
  :lfHeight, :long,
369
428
  :lfWidth, :long,
@@ -382,17 +441,44 @@ module WinGUI
382
441
  end
383
442
 
384
443
  attach_function :CreateFontIndirect, :CreateFontIndirectW, [
385
- :pointer
444
+ LOGFONT.by_ref(:in)
445
+ ], :pointer
446
+
447
+ class LOGBRUSH < FFI::Struct
448
+ extend Util::ScopedStruct
449
+
450
+ layout \
451
+ :lbStyle, :uint,
452
+ :lbColor, :ulong,
453
+ :lbHatch, :ulong
454
+ end
455
+
456
+ attach_function :CreateBrushIndirect, [
457
+ LOGBRUSH.by_ref(:in)
458
+ ], :pointer
459
+
460
+ class LOGPEN < FFI::Struct
461
+ extend Util::ScopedStruct
462
+
463
+ layout \
464
+ :lopnStyle, :uint,
465
+ :lopnWidth, POINT,
466
+ :lopnColor, :ulong
467
+ end
468
+
469
+ attach_function :CreatePenIndirect, [
470
+ LOGPEN.by_ref(:in)
386
471
  ], :pointer
387
472
 
388
473
  attach_function :DeleteObject, [
389
474
  :pointer
390
475
  ], :int
391
476
 
392
- attach_function :SelectObject, [
477
+ attach_function :GetObject, :GetObjectW, [
393
478
  :pointer,
479
+ :int,
394
480
  :pointer
395
- ], :pointer
481
+ ], :int
396
482
 
397
483
  attach_function :SetBkColor, [
398
484
  :pointer,
@@ -412,11 +498,24 @@ module WinGUI
412
498
  :pointer
413
499
  ], :ulong
414
500
 
501
+ attach_function :SelectObject, [
502
+ :pointer,
503
+ :pointer
504
+ ], :pointer
505
+
506
+ attach_function :TextOut, :TextOutW, [
507
+ :pointer,
508
+ :int,
509
+ :int,
510
+ :buffer_in,
511
+ :int
512
+ ], :int
513
+
415
514
  attach_function :MoveToEx, [
416
515
  :pointer,
417
516
  :int,
418
517
  :int,
419
- :pointer
518
+ POINT.by_ref(:out)
420
519
  ], :int
421
520
 
422
521
  attach_function :LineTo, [
@@ -424,7 +523,9 @@ module WinGUI
424
523
  :int,
425
524
  :int
426
525
  ], :int
526
+ #}
427
527
 
528
+ #{ user32
428
529
  ffi_lib 'user32'
429
530
  ffi_convention :stdcall
430
531
 
@@ -434,10 +535,95 @@ module WinGUI
434
535
  ], :int
435
536
 
436
537
  Detonate(0, :SetProcessDPIAware) unless
437
- Object.const_defined?(:DPI_AWARE) &&
438
- !DPI_AWARE
538
+ Object.const_defined?(:DPI_AWARE) && !DPI_AWARE
539
+ end
540
+
541
+ attach_function :GetDC, [
542
+ :pointer
543
+ ], :pointer
544
+
545
+ attach_function :ReleaseDC, [
546
+ :pointer,
547
+ :pointer
548
+ ], :int
549
+
550
+ #{ DPIxxx
551
+ Detonate(FFI::Pointer::NULL, :GetDC,
552
+ nil
553
+ ).tap { |hdc|
554
+ DPIX = GetDeviceCaps(hdc, LOGPIXELSX)
555
+ DPIY = GetDeviceCaps(hdc, LOGPIXELSY)
556
+
557
+ ReleaseDC(nil, hdc)
558
+ }
559
+ #}
560
+
561
+ def DPIAwareX(x)
562
+ MulDiv(x, DPIX, 96)
563
+ end
564
+
565
+ def DPIAwareY(y)
566
+ MulDiv(y, DPIY, 96)
567
+ end
568
+
569
+ def DPIAwareXY(*args)
570
+ raise ArgumentError, 'Expected two or more, even count arguments' if
571
+ args.length < 2 || args.length.odd?
572
+
573
+ args.each_with_index { |arg, i|
574
+ args[i] = (i.even?) ? DPIAwareX(arg) : DPIAwareY(arg)
575
+ }
576
+ end
577
+
578
+ module_function :DPIAwareX, :DPIAwareY, :DPIAwareXY
579
+
580
+ #{ SM_xxx
581
+ SM_CXSCREEN = 0
582
+ SM_CYSCREEN = 1
583
+ #}
584
+
585
+ attach_function :GetSystemMetrics, [
586
+ :int
587
+ ], :int
588
+
589
+ #{ SPI_xxx
590
+ class NONCLIENTMETRICS < FFI::Struct
591
+ extend Util::ScopedStruct
592
+
593
+ layout *[
594
+ :cbSize, :uint,
595
+ :iBorderWidth, :int,
596
+ :iScrollWidth, :int,
597
+ :iScrollHeight, :int,
598
+ :iCaptionWidth, :int,
599
+ :iCaptionHeight, :int,
600
+ :lfCaptionFont, LOGFONT,
601
+ :iSmCaptionWidth, :int,
602
+ :iSmCaptionHeight, :int,
603
+ :lfSmCaptionFont, LOGFONT,
604
+ :iMenuWidth, :int,
605
+ :iMenuHeight, :int,
606
+ :lfMenuFont, LOGFONT,
607
+ :lfStatusFont, LOGFONT,
608
+ :lfMessageFont, LOGFONT,
609
+ (WINVER >= WINVISTA) ? [:iPaddedBorderWidth, :int] : nil
610
+ ].tap { |layout|
611
+ layout.flatten!
612
+ layout.compact!
613
+ }
439
614
  end
440
615
 
616
+ SPI_SETNONCLIENTMETRICS = 0x002A
617
+ SPI_GETNONCLIENTMETRICS = 0x0029
618
+ #}
619
+
620
+ attach_function :SystemParametersInfo, :SystemParametersInfoW, [
621
+ :uint,
622
+ :uint,
623
+ :pointer,
624
+ :uint
625
+ ], :int
626
+
441
627
  #{ MB_xxx
442
628
  MB_OK = 0x00000000
443
629
  MB_OKCANCEL = 0x00000001
@@ -473,6 +659,9 @@ module WinGUI
473
659
  IDIGNORE = 5
474
660
  IDTRYAGAIN = 10
475
661
  IDCONTINUE = 11
662
+ if WINVER >= WINXP
663
+ IDTIMEOUT = 32000
664
+ end
476
665
  #}
477
666
 
478
667
  attach_function :MessageBox, :MessageBoxW, [
@@ -482,6 +671,20 @@ module WinGUI
482
671
  :uint
483
672
  ], :int
484
673
 
674
+ if WINVER >= WINXP
675
+ begin
676
+ attach_function :MessageBoxTimeout, :MessageBoxTimeoutW, [
677
+ :pointer,
678
+ :buffer_in,
679
+ :buffer_in,
680
+ :uint,
681
+ :ushort,
682
+ :ulong
683
+ ], :int
684
+ rescue FFI::NotFoundError # MessageBoxTimeout is undocumented
685
+ end
686
+ end
687
+
485
688
  #{ CS_xxx
486
689
  CS_DBLCLKS = 0x0008
487
690
  CS_HREDRAW = 0x0002
@@ -511,12 +714,21 @@ module WinGUI
511
714
  ], :long
512
715
 
513
716
  #{ IDI_xxx
717
+ IDI_WINLOGO = FFI::Pointer.new(32517)
514
718
  IDI_APPLICATION = FFI::Pointer.new(32512)
719
+ if WINVER >= WINVISTA
720
+ IDI_SHIELD = FFI::Pointer.new(32518)
721
+ end
722
+
723
+ IDI_INFORMATION = FFI::Pointer.new(32516)
724
+ IDI_WARNING = FFI::Pointer.new(32515)
725
+ IDI_ERROR = FFI::Pointer.new(32513)
726
+ IDI_QUESTION = FFI::Pointer.new(32514)
515
727
  #}
516
728
 
517
729
  attach_function :LoadIcon, :LoadIconW, [
518
730
  :pointer,
519
- :pointer
731
+ :buffer_in
520
732
  ], :pointer
521
733
 
522
734
  #{ IDC_xxx
@@ -524,6 +736,7 @@ module WinGUI
524
736
  IDC_WAIT = FFI::Pointer.new(32514)
525
737
  IDC_APPSTARTING = FFI::Pointer.new(32650)
526
738
  IDC_HAND = FFI::Pointer.new(32649)
739
+ IDC_CROSS = FFI::Pointer.new(32515)
527
740
  IDC_SIZEALL = FFI::Pointer.new(32646)
528
741
  IDC_SIZENS = FFI::Pointer.new(32645)
529
742
  IDC_SIZEWE = FFI::Pointer.new(32644)
@@ -533,7 +746,7 @@ module WinGUI
533
746
 
534
747
  attach_function :LoadCursor, :LoadCursorW, [
535
748
  :pointer,
536
- :pointer
749
+ :buffer_in
537
750
  ], :pointer
538
751
 
539
752
  #{ COLOR/CTLCOLOR_xxx
@@ -544,6 +757,8 @@ module WinGUI
544
757
  #}
545
758
 
546
759
  class WNDCLASSEX < FFI::Struct
760
+ extend Util::ScopedStruct
761
+
547
762
  layout \
548
763
  :cbSize, :uint,
549
764
  :style, :uint,
@@ -560,18 +775,18 @@ module WinGUI
560
775
  end
561
776
 
562
777
  attach_function :RegisterClassEx, :RegisterClassExW, [
563
- :pointer
778
+ WNDCLASSEX.by_ref(:in)
564
779
  ], :ushort
565
780
 
566
781
  attach_function :UnregisterClass, :UnregisterClassW, [
567
- :pointer,
782
+ :buffer_in,
568
783
  :pointer
569
784
  ], :int
570
785
 
571
786
  attach_function :GetClassInfoEx, :GetClassInfoExW, [
572
787
  :pointer,
573
- :pointer,
574
- :pointer
788
+ :buffer_in,
789
+ WNDCLASSEX.by_ref(:out)
575
790
  ], :int
576
791
 
577
792
  #{ WS_xxx
@@ -661,7 +876,7 @@ module WinGUI
661
876
 
662
877
  attach_function :GetClassName, :GetClassNameW, [
663
878
  :pointer,
664
- :pointer,
879
+ :buffer_out,
665
880
  :int
666
881
  ], :int
667
882
 
@@ -772,31 +987,31 @@ module WinGUI
772
987
 
773
988
  attach_function :GetWindowRect, [
774
989
  :pointer,
775
- :pointer
990
+ RECT.by_ref(:out)
776
991
  ], :int
777
992
 
778
993
  attach_function :GetClientRect, [
779
994
  :pointer,
780
- :pointer
995
+ RECT.by_ref(:out)
781
996
  ], :int
782
997
 
783
998
  attach_function :GetCursorPos, [
784
- :pointer
999
+ POINT.by_ref(:out)
785
1000
  ], :int
786
1001
 
787
1002
  attach_function :ScreenToClient, [
788
1003
  :pointer,
789
- :pointer
1004
+ POINT.by_ref
790
1005
  ], :int
791
1006
 
792
1007
  attach_function :ClientToScreen, [
793
1008
  :pointer,
794
- :pointer
1009
+ POINT.by_ref
795
1010
  ], :int
796
1011
 
797
1012
  attach_function :InvalidateRect, [
798
1013
  :pointer,
799
- :pointer,
1014
+ RECT.by_ref(:in),
800
1015
  :int
801
1016
  ], :int
802
1017
 
@@ -804,49 +1019,27 @@ module WinGUI
804
1019
  :pointer
805
1020
  ], :int
806
1021
 
807
- attach_function :GetDC, [
1022
+ attach_function :SetCapture, [
808
1023
  :pointer
809
1024
  ], :pointer
810
1025
 
811
- attach_function :ReleaseDC, [
812
- :pointer,
813
- :pointer
814
- ], :int
815
-
816
- Detonate(FFI::Pointer::NULL, :GetDC,
817
- nil
818
- ).tap { |hdc|
819
- DPIX = GetDeviceCaps(hdc, LOGPIXELSX)
820
- DPIY = GetDeviceCaps(hdc, LOGPIXELSY)
1026
+ attach_function :ReleaseCapture, [
821
1027
 
822
- ReleaseDC(nil, hdc)
823
- }
824
-
825
- def DPIScaleX(x)
826
- MulDiv(x, DPIX, 96)
827
- end
1028
+ ], :int
828
1029
 
829
- def DPIScaleY(y)
830
- MulDiv(y, DPIY, 96)
831
- end
1030
+ attach_function :GetCapture, [
832
1031
 
833
- def DPIScale(*args)
834
- case args.length
835
- when 2 # POINT, SIZE
836
- [DPIScaleX(args[0]), DPIScaleY(args[1])]
837
- when 4 # RECT
838
- [
839
- DPIScaleX(args[0]), DPIScaleY(args[1]),
840
- DPIScaleX(args[2]), DPIScaleY(args[3])
841
- ]
842
- else
843
- raise ArgumentError
844
- end
845
- end
1032
+ ], :pointer
846
1033
 
847
- module_function :DPIScaleX, :DPIScaleY, :DPIScale
1034
+ attach_function :GetUpdateRect, [
1035
+ :pointer,
1036
+ RECT.by_ref(:out),
1037
+ :int
1038
+ ], :int
848
1039
 
849
1040
  class PAINTSTRUCT < FFI::Struct
1041
+ extend Util::ScopedStruct
1042
+
850
1043
  layout \
851
1044
  :hdc, :pointer,
852
1045
  :fErase, :int,
@@ -858,12 +1051,12 @@ module WinGUI
858
1051
 
859
1052
  attach_function :BeginPaint, [
860
1053
  :pointer,
861
- :pointer
1054
+ PAINTSTRUCT.by_ref(:out)
862
1055
  ], :pointer
863
1056
 
864
1057
  attach_function :EndPaint, [
865
1058
  :pointer,
866
- :pointer
1059
+ PAINTSTRUCT.by_ref(:in)
867
1060
  ], :int
868
1061
 
869
1062
  attach_function :SetMenu, [
@@ -890,6 +1083,8 @@ module WinGUI
890
1083
 
891
1084
  #{ WM_xxx
892
1085
  class CREATESTRUCT < FFI::Struct
1086
+ extend Util::ScopedStruct
1087
+
893
1088
  layout \
894
1089
  :lpCreateParams, :pointer,
895
1090
  :hInstance, :pointer,
@@ -918,6 +1113,18 @@ module WinGUI
918
1113
 
919
1114
  WM_PAINT = 0x000F
920
1115
 
1116
+ def GET_X_LPARAM(lParam)
1117
+ ((x = LOWORD(lParam)) > 0x7fff) ?
1118
+ x - 0x1_0000 :
1119
+ x
1120
+ end
1121
+
1122
+ def GET_Y_LPARAM(lParam)
1123
+ ((y = HIWORD(lParam)) > 0x7fff) ?
1124
+ y - 0x1_0000 :
1125
+ y
1126
+ end
1127
+
921
1128
  WM_LBUTTONDOWN = 0x0201
922
1129
  WM_LBUTTONUP = 0x0202
923
1130
  WM_LBUTTONDBLCLK = 0x0203
@@ -932,6 +1139,8 @@ module WinGUI
932
1139
  WM_COMMAND = 0x0111
933
1140
 
934
1141
  class NMHDR < FFI::Struct
1142
+ extend Util::ScopedStruct
1143
+
935
1144
  layout \
936
1145
  :hwndFrom, :pointer,
937
1146
  :idFrom, :uint,
@@ -958,6 +1167,8 @@ module WinGUI
958
1167
  ], :int
959
1168
 
960
1169
  class MSG < FFI::Struct
1170
+ extend Util::ScopedStruct
1171
+
961
1172
  layout \
962
1173
  :hwnd, :pointer,
963
1174
  :message, :uint,
@@ -968,7 +1179,7 @@ module WinGUI
968
1179
  end
969
1180
 
970
1181
  attach_function :GetMessage, :GetMessageW, [
971
- :pointer,
1182
+ MSG.by_ref(:out),
972
1183
  :pointer,
973
1184
  :uint,
974
1185
  :uint
@@ -976,21 +1187,21 @@ module WinGUI
976
1187
 
977
1188
  attach_function :IsDialogMessage, [
978
1189
  :pointer,
979
- :pointer
1190
+ MSG.by_ref(:in)
980
1191
  ], :int
981
1192
 
982
1193
  attach_function :TranslateAccelerator, :TranslateAcceleratorW, [
983
1194
  :pointer,
984
1195
  :pointer,
985
- :pointer
1196
+ MSG.by_ref(:in)
986
1197
  ], :int
987
1198
 
988
1199
  attach_function :TranslateMessage, [
989
- :pointer
1200
+ MSG.by_ref(:in)
990
1201
  ], :int
991
1202
 
992
1203
  attach_function :DispatchMessage, :DispatchMessageW, [
993
- :pointer
1204
+ MSG.by_ref(:in)
994
1205
  ], :long
995
1206
 
996
1207
  attach_function :PostQuitMessage, [
@@ -1081,6 +1292,8 @@ module WinGUI
1081
1292
  #}
1082
1293
 
1083
1294
  class ACCEL < FFI::Struct
1295
+ extend Util::ScopedStruct
1296
+
1084
1297
  layout \
1085
1298
  :fVirt, :uchar,
1086
1299
  :key, :ushort,
@@ -1095,40 +1308,5 @@ module WinGUI
1095
1308
  attach_function :DestroyAcceleratorTable, [
1096
1309
  :pointer
1097
1310
  ], :int
1098
-
1099
- #{ SPI_xxx
1100
- class NONCLIENTMETRICS < FFI::Struct
1101
- layout *[
1102
- :cbSize, :uint,
1103
- :iBorderWidth, :int,
1104
- :iScrollWidth, :int,
1105
- :iScrollHeight, :int,
1106
- :iCaptionWidth, :int,
1107
- :iCaptionHeight, :int,
1108
- :lfCaptionFont, LOGFONT,
1109
- :iSmCaptionWidth, :int,
1110
- :iSmCaptionHeight, :int,
1111
- :lfSmCaptionFont, LOGFONT,
1112
- :iMenuWidth, :int,
1113
- :iMenuHeight, :int,
1114
- :lfMenuFont, LOGFONT,
1115
- :lfStatusFont, LOGFONT,
1116
- :lfMessageFont, LOGFONT,
1117
- (WINVER >= WINVISTA) ? [:iPaddedBorderWidth, :int] : nil
1118
- ].tap { |layout|
1119
- layout.flatten!
1120
- layout.compact!
1121
- }
1122
- end
1123
-
1124
- SPI_SETNONCLIENTMETRICS = 0x002A
1125
- SPI_GETNONCLIENTMETRICS = 0x0029
1126
1311
  #}
1127
-
1128
- attach_function :SystemParametersInfo, :SystemParametersInfoW, [
1129
- :uint,
1130
- :uint,
1131
- :pointer,
1132
- :uint
1133
- ], :int
1134
1312
  end