ffi-wingui-core 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010 - 2011 Radoslav Peev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,219 @@
1
+ require 'ffi-wingui-core'
2
+
3
+ include WinGUI
4
+
5
+ EnableVisualStyles()
6
+
7
+ APPNAME = File.basename(__FILE__, '.rbw')
8
+
9
+ CMD_ITEM1 = WM_APP + 1
10
+ CTL_BUTTON1 = CMD_ITEM1 + 1
11
+
12
+ WndExtra = Struct.new(
13
+ :haccel,
14
+ :hmf
15
+ )
16
+
17
+ class WndExtra
18
+ REFS = {}
19
+
20
+ def initialize(*args)
21
+ super
22
+
23
+ REFS[object_id] = self
24
+
25
+ ObjectSpace.define_finalizer(self, -> id {
26
+ REFS.delete(id)
27
+ })
28
+ end
29
+ end
30
+
31
+ def onCreate(hwnd,
32
+ cs
33
+ )
34
+ #xtra = ObjectSpace._id2ref(GetWindowLong(hwnd, GWL_USERDATA))
35
+ xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
36
+
37
+ hbar = CreateMenu()
38
+ hmenu1 = CreatePopupMenu()
39
+ AppendMenu(hmenu1, MF_STRING, CMD_ITEM1, "Item&1\tAlt+I")
40
+ AppendMenu(hbar, MF_POPUP, hmenu1.to_i, 'Menu&1')
41
+ SetMenu(hwnd, hbar)
42
+
43
+ accels = [
44
+ [FVIRTKEY | FALT, 'I'.ord, CMD_ITEM1]
45
+ ]
46
+
47
+ FFI::MemoryPointer.new(ACCEL, accels.size) { |paccels|
48
+ accels.each.with_index { |data, i|
49
+ accel = ACCEL.new(paccels + i * ACCEL.size)
50
+
51
+ accel[:fVirt], accel[:key], accel[:cmd] = data
52
+ }
53
+
54
+ xtra[:haccel] = CreateAcceleratorTable(paccels, accels.size)
55
+ }
56
+
57
+ ncm = NONCLIENTMETRICS.new; ncm[:cbSize] = ncm.size
58
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.size, ncm, 0);
59
+ xtra[:hmf] = CreateFontIndirect(ncm[:lfMenuFont]);
60
+
61
+ hbtn1 = CreateWindowEx(
62
+ 0, 'Button', '&Button1', WS_CHILD | WS_VISIBLE,
63
+ 10, 10, 100, 25,
64
+ hwnd, FFI::Pointer.new(CTL_BUTTON1), GetModuleHandle(nil), nil
65
+ )
66
+ SendMessage(hbtn1, WM_SETFONT, xtra[:hmf].to_i, 1)
67
+
68
+ 0
69
+ end
70
+
71
+ def onDestroy(hwnd)
72
+ #xtra = ObjectSpace._id2ref(GetWindowLong(hwnd, GWL_USERDATA))
73
+ xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
74
+
75
+ DestroyAcceleratorTable(xtra[:haccel])
76
+ DeleteObject(xtra[:hmf])
77
+
78
+ PostQuitMessage(0); 0
79
+ end
80
+
81
+ def onItem1(verb,
82
+ hctl, hwnd
83
+ )
84
+ MessageBox(hwnd,
85
+ __method__.to_s,
86
+ APPNAME,
87
+ MB_ICONINFORMATION
88
+ )
89
+
90
+ =begin
91
+ verb:
92
+ 0 - menu
93
+ 1 - accelerator
94
+ =end
95
+
96
+ raise 'WM_COMMAND hctl must be NULL for menu/accelerator' unless
97
+ hctl.null?
98
+
99
+ EnableWindow(GetDlgItem(hwnd, CTL_BUTTON1), 1)
100
+ EnableMenuItem(GetMenu(hwnd), CMD_ITEM1, MF_GRAYED)
101
+
102
+ 0
103
+ end
104
+
105
+ def onButton1(verb,
106
+ hctl, hwnd
107
+ )
108
+ MessageBox(hwnd,
109
+ __method__.to_s,
110
+ APPNAME,
111
+ MB_ICONINFORMATION
112
+ )
113
+
114
+ =begin
115
+ verb:
116
+ BN_xxx
117
+ =end
118
+
119
+ raise 'WM_COMMAND hctl must NOT be NULL for control' if
120
+ hctl.null?
121
+
122
+ EnableMenuItem(GetMenu(hwnd), CMD_ITEM1, MF_ENABLED)
123
+ EnableWindow(hctl, 0)
124
+
125
+ 0
126
+ end
127
+
128
+ def windowProc(hwnd,
129
+ uMsg,
130
+ wParam, lParam
131
+ )
132
+ case uMsg
133
+ when WM_NCCREATE
134
+ DefWindowProc(hwnd, uMsg, wParam, lParam)
135
+
136
+ SetWindowLong(hwnd,
137
+ GWL_USERDATA,
138
+ CREATESTRUCT.new(FFI::Pointer.new(lParam))[:lpCreateParams].to_i
139
+ )
140
+
141
+ return 1
142
+ when WM_CREATE
143
+ return onCreate(hwnd, CREATESTRUCT.new(FFI::Pointer.new(lParam)))
144
+ when WM_DESTROY
145
+ return onDestroy(hwnd)
146
+ when WM_COMMAND
147
+ id, verb = LOWORD(wParam), HIWORD(wParam)
148
+ hctl = FFI::Pointer.new(lParam)
149
+
150
+ case id
151
+ when CMD_ITEM1
152
+ return onItem1(verb, hctl, hwnd)
153
+ when CTL_BUTTON1
154
+ return onButton1(verb, hctl, hwnd)
155
+ end
156
+ end
157
+
158
+ DefWindowProc(hwnd, uMsg, wParam, lParam)
159
+ rescue
160
+ MessageBox(nil,
161
+ %'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}',
162
+ APPNAME,
163
+ MB_ICONERROR
164
+ ); PostQuitMessage(2)
165
+ end
166
+
167
+ def main
168
+ wc = WNDCLASSEX.new
169
+ xtra = WndExtra.new
170
+
171
+ wc[:cbSize] = wc.size
172
+ wc[:lpfnWndProc] = method(:windowProc)
173
+ #wc[:cbWndExtra] = FFI::Pointer.size
174
+ wc[:cbWndExtra] = FFI::Type::Builtin::POINTER.size
175
+ wc[:hInstance] = GetModuleHandle(nil)
176
+ wc[:hIcon] = LoadIcon(nil, IDI_APPLICATION)
177
+ wc[:hCursor] = LoadCursor(nil, IDC_ARROW)
178
+ wc[:hbrBackground] = FFI::Pointer.new(CTLCOLOR_DLG + 1)
179
+ wc[:lpszClassName] = className = FFI::MemoryPointer.from_string(
180
+ APPNAME
181
+ )
182
+
183
+ DetonateLastError(0, :RegisterClassEx,
184
+ wc
185
+ ) { className.free }
186
+
187
+ hwnd = CreateWindowEx(
188
+ 0, APPNAME, APPNAME, WS_OVERLAPPEDWINDOW,
189
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
190
+ nil, nil, GetModuleHandle(nil), FFI::Pointer.new(xtra.object_id)
191
+ )
192
+
193
+ raise "CreateWindowEx failed (last error: #{GetLastError()})" if
194
+ hwnd.null? && GetLastError() != 0
195
+
196
+ ShowWindow(hwnd, SW_SHOWNORMAL)
197
+ UpdateWindow(hwnd)
198
+
199
+ msg = MSG.new
200
+
201
+ until DetonateLastError(-1, :GetMessage,
202
+ msg, nil, 0, 0
203
+ ) == 0
204
+ if TranslateAccelerator(hwnd, xtra[:haccel], msg) == 0
205
+ TranslateMessage(msg)
206
+ DispatchMessage(msg)
207
+ end
208
+ end
209
+
210
+ exit(msg[:wParam])
211
+ rescue
212
+ MessageBox(nil,
213
+ %'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}',
214
+ APPNAME,
215
+ MB_ICONERROR
216
+ ); exit(1)
217
+ end
218
+
219
+ main
@@ -0,0 +1,11 @@
1
+ require 'ffi-wingui-core'
2
+
3
+ include WinGUI
4
+
5
+ EnableVisualStyles()
6
+
7
+ MessageBox(nil,
8
+ 'Hello, world!',
9
+ 'Hello',
10
+ MB_ICONINFORMATION
11
+ )
@@ -0,0 +1,115 @@
1
+ require 'ffi-wingui-core'
2
+
3
+ include WinGUI
4
+
5
+ EnableVisualStyles()
6
+
7
+ APPNAME = File.basename(__FILE__, '.rbw')
8
+
9
+ def onCreate(hwnd,
10
+ cs
11
+ )
12
+ answer = MessageBox(nil,
13
+ 'Create?',
14
+ cs[:lpszName].read_string,
15
+ MB_YESNO | MB_ICONQUESTION
16
+ )
17
+
18
+ return -1 if answer == IDNO
19
+
20
+ 0
21
+ end
22
+
23
+ def onClose(hwnd)
24
+ answer = MessageBox(hwnd,
25
+ 'Close?',
26
+ APPNAME,
27
+ MB_YESNO | MB_ICONQUESTION |
28
+ MB_DEFBUTTON2
29
+ )
30
+
31
+ DestroyWindow(hwnd) if answer == IDYES
32
+
33
+ 0
34
+ end
35
+
36
+ def onDestroy(hwnd)
37
+ MessageBox(nil,
38
+ __method__.to_s,
39
+ APPNAME,
40
+ MB_ICONINFORMATION
41
+ )
42
+
43
+ PostQuitMessage(0); 0
44
+ end
45
+
46
+ def windowProc(hwnd,
47
+ uMsg,
48
+ wParam, lParam
49
+ )
50
+ case uMsg
51
+ when WM_CREATE
52
+ return onCreate(hwnd, CREATESTRUCT.new(FFI::Pointer.new(lParam)))
53
+ when WM_CLOSE
54
+ return onClose(hwnd)
55
+ when WM_DESTROY
56
+ return onDestroy(hwnd)
57
+ end
58
+
59
+ DefWindowProc(hwnd, uMsg, wParam, lParam)
60
+ rescue
61
+ MessageBox(nil,
62
+ %'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}',
63
+ APPNAME,
64
+ MB_ICONERROR
65
+ ); PostQuitMessage(2)
66
+ end
67
+
68
+ def main
69
+ wc = WNDCLASSEX.new
70
+
71
+ wc[:cbSize] = wc.size
72
+ wc[:lpfnWndProc] = method(:windowProc)
73
+ wc[:hInstance] = GetModuleHandle(nil)
74
+ wc[:hIcon] = LoadIcon(nil, IDI_APPLICATION)
75
+ wc[:hCursor] = LoadCursor(nil, IDC_ARROW)
76
+ wc[:hbrBackground] = FFI::Pointer.new(COLOR_WINDOW + 1)
77
+ wc[:lpszClassName] = className = FFI::MemoryPointer.from_string(
78
+ APPNAME
79
+ )
80
+
81
+ DetonateLastError(0, :RegisterClassEx,
82
+ wc
83
+ ) { className.free }
84
+
85
+ hwnd = CreateWindowEx(
86
+ 0, APPNAME, APPNAME, WS_OVERLAPPEDWINDOW,
87
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
88
+ nil, nil, GetModuleHandle(nil), nil
89
+ )
90
+
91
+ raise "CreateWindowEx failed (last error: #{GetLastError()})" if
92
+ hwnd.null? && GetLastError() != 0
93
+
94
+ ShowWindow(hwnd, SW_SHOWNORMAL)
95
+ UpdateWindow(hwnd)
96
+
97
+ msg = MSG.new
98
+
99
+ until DetonateLastError(-1, :GetMessage,
100
+ msg, nil, 0, 0
101
+ ) == 0
102
+ TranslateMessage(msg)
103
+ DispatchMessage(msg)
104
+ end
105
+
106
+ exit(msg[:wParam])
107
+ rescue
108
+ MessageBox(nil,
109
+ %'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}',
110
+ APPNAME,
111
+ MB_ICONERROR
112
+ ); exit(1)
113
+ end
114
+
115
+ main
@@ -0,0 +1,145 @@
1
+ require 'ffi-wingui-core'
2
+
3
+ include WinGUI
4
+
5
+ EnableVisualStyles()
6
+
7
+ APPNAME = File.basename(__FILE__, '.rbw')
8
+
9
+ def onNCCreate(hwnd,
10
+ cs
11
+ )
12
+ answer = MessageBox(nil,
13
+ 'NCCreate?',
14
+ cs[:lpszName].read_string,
15
+ MB_YESNO | MB_ICONQUESTION
16
+ )
17
+
18
+ return 0 if answer == IDNO
19
+
20
+ 1
21
+ end
22
+
23
+ def onCreate(hwnd,
24
+ cs
25
+ )
26
+ answer = MessageBox(nil,
27
+ 'Create?',
28
+ cs[:lpszName].read_string,
29
+ MB_YESNO | MB_ICONQUESTION
30
+ )
31
+
32
+ return -1 if answer == IDNO
33
+
34
+ 0
35
+ end
36
+
37
+ def onClose(hwnd)
38
+ answer = MessageBox(hwnd,
39
+ 'Close?',
40
+ APPNAME,
41
+ MB_YESNO | MB_ICONQUESTION |
42
+ MB_DEFBUTTON2
43
+ )
44
+
45
+ DestroyWindow(hwnd) if answer == IDYES
46
+
47
+ 0
48
+ end
49
+
50
+ def onDestroy(hwnd)
51
+ MessageBox(nil,
52
+ __method__.to_s,
53
+ APPNAME,
54
+ MB_ICONINFORMATION
55
+ )
56
+
57
+ 0
58
+ end
59
+
60
+ def onNCDestroy(hwnd)
61
+ MessageBox(nil,
62
+ __method__.to_s,
63
+ APPNAME,
64
+ MB_ICONINFORMATION
65
+ )
66
+
67
+ PostQuitMessage(0); 0
68
+ end
69
+
70
+ def windowProc(hwnd,
71
+ uMsg,
72
+ wParam, lParam
73
+ )
74
+ case uMsg
75
+ when WM_NCCREATE
76
+ DefWindowProc(hwnd, uMsg, wParam, lParam)
77
+
78
+ return onNCCreate(hwnd, CREATESTRUCT.new(FFI::Pointer.new(lParam)))
79
+ when WM_CREATE
80
+ return onCreate(hwnd, CREATESTRUCT.new(FFI::Pointer.new(lParam)))
81
+ when WM_CLOSE
82
+ return onClose(hwnd)
83
+ when WM_DESTROY
84
+ return onDestroy(hwnd)
85
+ when WM_NCDESTROY
86
+ return onNCDestroy(hwnd)
87
+ end
88
+
89
+ DefWindowProc(hwnd, uMsg, wParam, lParam)
90
+ rescue
91
+ MessageBox(nil,
92
+ %'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}',
93
+ APPNAME,
94
+ MB_ICONERROR
95
+ ); PostQuitMessage(2)
96
+ end
97
+
98
+ def main
99
+ wc = WNDCLASSEX.new
100
+
101
+ wc[:cbSize] = wc.size
102
+ wc[:lpfnWndProc] = method(:windowProc)
103
+ wc[:hInstance] = GetModuleHandle(nil)
104
+ wc[:hIcon] = LoadIcon(nil, IDI_APPLICATION)
105
+ wc[:hCursor] = LoadCursor(nil, IDC_ARROW)
106
+ wc[:hbrBackground] = FFI::Pointer.new(COLOR_WINDOW + 1)
107
+ wc[:lpszClassName] = className = FFI::MemoryPointer.from_string(
108
+ APPNAME
109
+ )
110
+
111
+ DetonateLastError(0, :RegisterClassEx,
112
+ wc
113
+ ) { className.free }
114
+
115
+ hwnd = CreateWindowEx(
116
+ 0, APPNAME, APPNAME, WS_OVERLAPPEDWINDOW,
117
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
118
+ nil, nil, GetModuleHandle(nil), nil
119
+ )
120
+
121
+ raise "CreateWindowEx failed (last error: #{GetLastError()})" if
122
+ hwnd.null? && GetLastError() != 0
123
+
124
+ ShowWindow(hwnd, SW_SHOWNORMAL)
125
+ UpdateWindow(hwnd)
126
+
127
+ msg = MSG.new
128
+
129
+ until DetonateLastError(-1, :GetMessage,
130
+ msg, nil, 0, 0
131
+ ) == 0
132
+ TranslateMessage(msg)
133
+ DispatchMessage(msg)
134
+ end
135
+
136
+ exit(msg[:wParam])
137
+ rescue
138
+ MessageBox(nil,
139
+ %'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}',
140
+ APPNAME,
141
+ MB_ICONERROR
142
+ ); exit(1)
143
+ end
144
+
145
+ main
@@ -0,0 +1,78 @@
1
+ require 'ffi-wingui-core'
2
+
3
+ include WinGUI
4
+
5
+ EnableVisualStyles()
6
+
7
+ APPNAME = File.basename(__FILE__, '.rbw')
8
+
9
+ def onDestroy(hwnd)
10
+ PostQuitMessage(0); 0
11
+ end
12
+
13
+ def windowProc(hwnd,
14
+ uMsg,
15
+ wParam, lParam
16
+ )
17
+ case uMsg
18
+ when WM_DESTROY
19
+ return onDestroy(hwnd)
20
+ end
21
+
22
+ DefWindowProc(hwnd, uMsg, wParam, lParam)
23
+ rescue
24
+ MessageBox(nil,
25
+ %'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}',
26
+ APPNAME,
27
+ MB_ICONERROR
28
+ ); PostQuitMessage(2)
29
+ end
30
+
31
+ def main
32
+ wc = WNDCLASSEX.new
33
+
34
+ wc[:cbSize] = wc.size
35
+ wc[:lpfnWndProc] = method(:windowProc)
36
+ wc[:hInstance] = GetModuleHandle(nil)
37
+ wc[:hIcon] = LoadIcon(nil, IDI_APPLICATION)
38
+ wc[:hCursor] = LoadCursor(nil, IDC_ARROW)
39
+ wc[:hbrBackground] = FFI::Pointer.new(COLOR_WINDOW + 1)
40
+ wc[:lpszClassName] = className = FFI::MemoryPointer.from_string(
41
+ APPNAME
42
+ )
43
+
44
+ DetonateLastError(0, :RegisterClassEx,
45
+ wc
46
+ ) { className.free }
47
+
48
+ hwnd = CreateWindowEx(
49
+ 0, APPNAME, APPNAME, WS_OVERLAPPEDWINDOW,
50
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
51
+ nil, nil, GetModuleHandle(nil), nil
52
+ )
53
+
54
+ raise "CreateWindowEx failed (last error: #{GetLastError()})" if
55
+ hwnd.null? && GetLastError() != 0
56
+
57
+ ShowWindow(hwnd, SW_SHOWNORMAL)
58
+ UpdateWindow(hwnd)
59
+
60
+ msg = MSG.new
61
+
62
+ until DetonateLastError(-1, :GetMessage,
63
+ msg, nil, 0, 0
64
+ ) == 0
65
+ TranslateMessage(msg)
66
+ DispatchMessage(msg)
67
+ end
68
+
69
+ exit(msg[:wParam])
70
+ rescue
71
+ MessageBox(nil,
72
+ %'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}',
73
+ APPNAME,
74
+ MB_ICONERROR
75
+ ); exit(1)
76
+ end
77
+
78
+ main
@@ -0,0 +1,130 @@
1
+ require 'ffi-wingui-core'
2
+
3
+ include WinGUI
4
+
5
+ EnableVisualStyles()
6
+
7
+ APPNAME = File.basename(__FILE__, '.rbw')
8
+
9
+ WndExtra = Struct.new(
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
26
+
27
+ def onCreate(hwnd,
28
+ cs
29
+ )
30
+ #xtra = ObjectSpace._id2ref(GetWindowLong(hwnd, GWL_USERDATA))
31
+ xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
32
+
33
+ xtra[:foo] = 'Foo'
34
+
35
+ -1
36
+ end
37
+
38
+ def onDestroy(hwnd)
39
+ #xtra = ObjectSpace._id2ref(GetWindowLong(hwnd, GWL_USERDATA))
40
+ xtra = WndExtra::REFS[GetWindowLong(hwnd, GWL_USERDATA)]
41
+
42
+ MessageBox(nil,
43
+ xtra[:foo],
44
+ APPNAME,
45
+ MB_ICONINFORMATION
46
+ )
47
+
48
+ PostQuitMessage(0); 0
49
+ end
50
+
51
+ def windowProc(hwnd,
52
+ uMsg,
53
+ wParam, lParam
54
+ )
55
+ case uMsg
56
+ when WM_NCCREATE
57
+ DefWindowProc(hwnd, uMsg, wParam, lParam)
58
+
59
+ SetWindowLong(hwnd,
60
+ GWL_USERDATA,
61
+ CREATESTRUCT.new(FFI::Pointer.new(lParam))[:lpCreateParams].to_i
62
+ )
63
+
64
+ return 1
65
+ when WM_CREATE
66
+ return onCreate(hwnd, CREATESTRUCT.new(FFI::Pointer.new(lParam)))
67
+ when WM_DESTROY
68
+ return onDestroy(hwnd)
69
+ end
70
+
71
+ DefWindowProc(hwnd, uMsg, wParam, lParam)
72
+ rescue
73
+ MessageBox(nil,
74
+ %'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}',
75
+ APPNAME,
76
+ MB_ICONERROR
77
+ ); PostQuitMessage(2)
78
+ end
79
+
80
+ def main
81
+ wc = WNDCLASSEX.new
82
+ xtra = WndExtra.new
83
+
84
+ wc[:cbSize] = wc.size
85
+ wc[:lpfnWndProc] = method(:windowProc)
86
+ #wc[:cbWndExtra] = FFI::Pointer.size
87
+ wc[:cbWndExtra] = FFI::Type::Builtin::POINTER.size
88
+ wc[:hInstance] = GetModuleHandle(nil)
89
+ wc[:hIcon] = LoadIcon(nil, IDI_APPLICATION)
90
+ wc[:hCursor] = LoadCursor(nil, IDC_ARROW)
91
+ wc[:hbrBackground] = FFI::Pointer.new(COLOR_WINDOW + 1)
92
+ wc[:lpszClassName] = className = FFI::MemoryPointer.from_string(
93
+ APPNAME
94
+ )
95
+
96
+ DetonateLastError(0, :RegisterClassEx,
97
+ wc
98
+ ) { className.free }
99
+
100
+ hwnd = CreateWindowEx(
101
+ 0, APPNAME, APPNAME, WS_OVERLAPPEDWINDOW,
102
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
103
+ nil, nil, GetModuleHandle(nil), FFI::Pointer.new(xtra.object_id)
104
+ )
105
+
106
+ raise "CreateWindowEx failed (last error: #{GetLastError()})" if
107
+ hwnd.null? && GetLastError() != 0
108
+
109
+ ShowWindow(hwnd, SW_SHOWNORMAL)
110
+ UpdateWindow(hwnd)
111
+
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
120
+
121
+ exit(msg[:wParam])
122
+ rescue
123
+ MessageBox(nil,
124
+ %'#{$!.to_s}\n\n#{$!.backtrace.join("\n")}',
125
+ APPNAME,
126
+ MB_ICONERROR
127
+ ); exit(1)
128
+ end
129
+
130
+ main