ffi-wingui-core 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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