5 #include <CoreGraphics/CoreGraphics.h>
6 #include <objc/NSObjCRuntime.h>
7 #include <objc/objc-runtime.h>
11 #define _DEFAULT_SOURCE 1
12 #include <X11/XKBlib.h>
14 #include <X11/keysym.h>
27 int keys
[256]; /* keys are mostly ASCII, but arrows are 17..20 */
28 int mod
; /* mod is 4 bits mask, ctrl=1, shift=2, alt=4, meta=8 */
32 #if defined(__APPLE__)
45 #define FENSTER_API extern
47 FENSTER_API
int fenster_open(struct fenster
*f
);
48 FENSTER_API
int fenster_loop(struct fenster
*f
);
49 FENSTER_API
void fenster_close(struct fenster
*f
);
50 FENSTER_API
void fenster_sleep(int64_t ms
);
51 FENSTER_API
int64_t fenster_time(void);
52 #define fenster_pixel(f, x, y) ((f)->buf[((y) * (f)->width) + (x)])
54 #ifndef FENSTER_HEADER
55 #if defined(__APPLE__)
56 #define msg(r, o, s) ((r(*)(id, SEL))objc_msgSend)(o, sel_getUid(s))
57 #define msg1(r, o, s, A, a) \
58 ((r(*)(id, SEL, A))objc_msgSend)(o, sel_getUid(s), a)
59 #define msg2(r, o, s, A, a, B, b) \
60 ((r(*)(id, SEL, A, B))objc_msgSend)(o, sel_getUid(s), a, b)
61 #define msg3(r, o, s, A, a, B, b, C, c) \
62 ((r(*)(id, SEL, A, B, C))objc_msgSend)(o, sel_getUid(s), a, b, c)
63 #define msg4(r, o, s, A, a, B, b, C, c, D, d) \
64 ((r(*)(id, SEL, A, B, C, D))objc_msgSend)(o, sel_getUid(s), a, b, c, d)
66 #define cls(x) ((id)objc_getClass(x))
68 extern id
const NSDefaultRunLoopMode
;
69 extern id
const NSApp
;
71 static void fenster_draw_rect(id v
, SEL s
, CGRect r
) {
73 struct fenster
*f
= (struct fenster
*)objc_getAssociatedObject(v
, "fenster");
74 CGContextRef context
=
75 msg(CGContextRef
, msg(id
, cls("NSGraphicsContext"), "currentContext"),
77 CGColorSpaceRef space
= CGColorSpaceCreateDeviceRGB();
78 CGDataProviderRef provider
= CGDataProviderCreateWithData(
79 NULL
, f
->buf
, f
->width
* f
->height
* 4, NULL
);
81 CGImageCreate(f
->width
, f
->height
, 8, 32, f
->width
* 4, space
,
82 kCGImageAlphaNoneSkipFirst
| kCGBitmapByteOrder32Little
,
83 provider
, NULL
, false, kCGRenderingIntentDefault
);
84 CGColorSpaceRelease(space
);
85 CGDataProviderRelease(provider
);
86 CGContextDrawImage(context
, CGRectMake(0, 0, f
->width
, f
->height
), img
);
90 static BOOL
fenster_should_close(id v
, SEL s
, id w
) {
91 (void)v
, (void)s
, (void)w
;
92 msg1(void, NSApp
, "terminate:", id
, NSApp
);
96 FENSTER_API
int fenster_open(struct fenster
*f
) {
97 msg(id
, cls("NSApplication"), "sharedApplication");
98 msg1(void, NSApp
, "setActivationPolicy:", NSInteger
, 0);
99 f
->wnd
= msg4(id
, msg(id
, cls("NSWindow"), "alloc"),
100 "initWithContentRect:styleMask:backing:defer:", CGRect
,
101 CGRectMake(0, 0, f
->width
, f
->height
), NSUInteger
, 3,
102 NSUInteger
, 2, BOOL
, NO
);
104 objc_allocateClassPair((Class
)cls("NSObject"), "FensterDelegate", 0);
105 class_addMethod(windelegate
, sel_getUid("windowShouldClose:"),
106 (IMP
)fenster_should_close
, "c@:@");
107 objc_registerClassPair(windelegate
);
108 msg1(void, f
->wnd
, "setDelegate:", id
,
109 msg(id
, msg(id
, (id
)windelegate
, "alloc"), "init"));
110 Class c
= objc_allocateClassPair((Class
)cls("NSView"), "FensterView", 0);
111 class_addMethod(c
, sel_getUid("drawRect:"), (IMP
)fenster_draw_rect
, "i@:@@");
112 objc_registerClassPair(c
);
114 id v
= msg(id
, msg(id
, (id
)c
, "alloc"), "init");
115 msg1(void, f
->wnd
, "setContentView:", id
, v
);
116 objc_setAssociatedObject(v
, "fenster", (id
)f
, OBJC_ASSOCIATION_ASSIGN
);
118 id title
= msg1(id
, cls("NSString"), "stringWithUTF8String:", const char *,
120 msg1(void, f
->wnd
, "setTitle:", id
, title
);
121 msg1(void, f
->wnd
, "makeKeyAndOrderFront:", id
, nil
);
122 msg(void, f
->wnd
, "center");
123 msg1(void, NSApp
, "activateIgnoringOtherApps:", BOOL
, YES
);
127 FENSTER_API
void fenster_close(struct fenster
*f
) {
128 msg(void, f
->wnd
, "close");
132 static const uint8_t FENSTER_KEYCODES
[128] = {65,83,68,70,72,71,90,88,67,86,0,66,81,87,69,82,89,84,49,50,51,52,54,53,61,57,55,45,56,48,93,79,85,91,73,80,10,76,74,39,75,59,92,44,47,78,77,46,9,32,96,8,0,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,2,3,127,0,5,0,4,0,20,19,18,17,0};
134 FENSTER_API
int fenster_loop(struct fenster
*f
) {
135 msg1(void, msg(id
, f
->wnd
, "contentView"), "setNeedsDisplay:", BOOL
, YES
);
136 id ev
= msg4(id
, NSApp
,
137 "nextEventMatchingMask:untilDate:inMode:dequeue:", NSUInteger
,
138 NSUIntegerMax
, id
, NULL
, id
, NSDefaultRunLoopMode
, BOOL
, YES
);
141 NSUInteger evtype
= msg(NSUInteger
, ev
, "type");
143 case 1: /* NSEventTypeMouseDown */
146 case 2: /* NSEventTypeMouseUp*/
150 case 6: { /* NSEventTypeMouseMoved */
151 CGPoint xy
= msg(CGPoint
, ev
, "locationInWindow");
153 f
->y
= (int)(f
->height
- xy
.y
);
156 case 10: /*NSEventTypeKeyDown*/
157 case 11: /*NSEventTypeKeyUp:*/ {
158 NSUInteger k
= msg(NSUInteger
, ev
, "keyCode");
159 f
->keys
[k
< 127 ? FENSTER_KEYCODES
[k
] : 0] = evtype
== 10;
160 NSUInteger mod
= msg(NSUInteger
, ev
, "modifierFlags") >> 17;
161 f
->mod
= (mod
& 0xc) | ((mod
& 1) << 1) | ((mod
>> 1) & 1);
165 msg1(void, NSApp
, "sendEvent:", id
, ev
);
168 #elif defined(_WIN32)
170 static const uint8_t FENSTER_KEYCODES
[] = {0,27,49,50,51,52,53,54,55,56,57,48,45,61,8,9,81,87,69,82,84,89,85,73,79,80,91,93,10,0,65,83,68,70,71,72,74,75,76,59,39,96,0,92,90,88,67,86,66,78,77,44,46,47,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,17,3,0,20,0,19,0,5,18,4,26,127};
172 typedef struct BINFO
{
173 BITMAPINFOHEADER bmiHeader
;
174 RGBQUAD bmiColors
[3];
176 static LRESULT CALLBACK
fenster_wndproc(HWND hwnd
, UINT msg
, WPARAM wParam
,
178 struct fenster
*f
= (struct fenster
*)GetWindowLongPtr(hwnd
, GWLP_USERDATA
);
182 HDC hdc
= BeginPaint(hwnd
, &ps
);
183 HDC memdc
= CreateCompatibleDC(hdc
);
184 HBITMAP hbmp
= CreateCompatibleBitmap(hdc
, f
->width
, f
->height
);
185 HBITMAP oldbmp
= SelectObject(memdc
, hbmp
);
186 BINFO bi
= {{sizeof(bi
), f
->width
, -f
->height
, 1, 32, BI_BITFIELDS
}};
187 bi
.bmiColors
[0].rgbRed
= 0xff;
188 bi
.bmiColors
[1].rgbGreen
= 0xff;
189 bi
.bmiColors
[2].rgbBlue
= 0xff;
190 SetDIBitsToDevice(memdc
, 0, 0, f
->width
, f
->height
, 0, 0, 0, f
->height
,
191 f
->buf
, (BITMAPINFO
*)&bi
, DIB_RGB_COLORS
);
192 BitBlt(hdc
, 0, 0, f
->width
, f
->height
, memdc
, 0, 0, SRCCOPY
);
193 SelectObject(memdc
, oldbmp
);
203 f
->mouse
= (msg
== WM_LBUTTONDOWN
);
206 f
->y
= HIWORD(lParam
), f
->x
= LOWORD(lParam
);
210 f
->mod
= ((GetKeyState(VK_CONTROL
) & 0x8000) >> 15) |
211 ((GetKeyState(VK_SHIFT
) & 0x8000) >> 14) |
212 ((GetKeyState(VK_MENU
) & 0x8000) >> 13) |
213 (((GetKeyState(VK_LWIN
) | GetKeyState(VK_RWIN
)) & 0x8000) >> 12);
214 f
->keys
[FENSTER_KEYCODES
[HIWORD(lParam
) & 0x1ff]] = !((lParam
>> 31) & 1);
220 return DefWindowProc(hwnd
, msg
, wParam
, lParam
);
225 FENSTER_API
int fenster_open(struct fenster
*f
) {
226 HINSTANCE hInstance
= GetModuleHandle(NULL
);
228 wc
.cbSize
= sizeof(WNDCLASSEX
);
229 wc
.style
= CS_VREDRAW
| CS_HREDRAW
;
230 wc
.lpfnWndProc
= fenster_wndproc
;
231 wc
.hInstance
= hInstance
;
232 wc
.lpszClassName
= f
->title
;
233 RegisterClassEx(&wc
);
234 f
->hwnd
= CreateWindowEx(WS_EX_CLIENTEDGE
, f
->title
, f
->title
,
235 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, CW_USEDEFAULT
,
236 f
->width
, f
->height
, NULL
, NULL
, hInstance
, NULL
);
240 SetWindowLongPtr(f
->hwnd
, GWLP_USERDATA
, (LONG_PTR
)f
);
241 ShowWindow(f
->hwnd
, SW_NORMAL
);
242 UpdateWindow(f
->hwnd
);
246 FENSTER_API
void fenster_close(struct fenster
*f
) { (void)f
; }
248 FENSTER_API
int fenster_loop(struct fenster
*f
) {
250 while (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
)) {
251 if (msg
.message
== WM_QUIT
)
253 TranslateMessage(&msg
);
254 DispatchMessage(&msg
);
256 InvalidateRect(f
->hwnd
, NULL
, TRUE
);
261 static int FENSTER_KEYCODES
[124] = {XK_BackSpace
,8,XK_Delete
,127,XK_Down
,18,XK_End
,5,XK_Escape
,27,XK_Home
,2,XK_Insert
,26,XK_Left
,20,XK_Page_Down
,4,XK_Page_Up
,3,XK_Return
,10,XK_Right
,19,XK_Tab
,9,XK_Up
,17,XK_apostrophe
,39,XK_backslash
,92,XK_bracketleft
,91,XK_bracketright
,93,XK_comma
,44,XK_equal
,61,XK_grave
,96,XK_minus
,45,XK_period
,46,XK_semicolon
,59,XK_slash
,47,XK_space
,32,XK_a
,65,XK_b
,66,XK_c
,67,XK_d
,68,XK_e
,69,XK_f
,70,XK_g
,71,XK_h
,72,XK_i
,73,XK_j
,74,XK_k
,75,XK_l
,76,XK_m
,77,XK_n
,78,XK_o
,79,XK_p
,80,XK_q
,81,XK_r
,82,XK_s
,83,XK_t
,84,XK_u
,85,XK_v
,86,XK_w
,87,XK_x
,88,XK_y
,89,XK_z
,90,XK_0
,48,XK_1
,49,XK_2
,50,XK_3
,51,XK_4
,52,XK_5
,53,XK_6
,54,XK_7
,55,XK_8
,56,XK_9
,57};
263 FENSTER_API
int fenster_open(struct fenster
*f
) {
264 f
->dpy
= XOpenDisplay(NULL
);
265 int screen
= DefaultScreen(f
->dpy
);
266 f
->w
= XCreateSimpleWindow(f
->dpy
, RootWindow(f
->dpy
, screen
), 0, 0, f
->width
,
267 f
->height
, 0, BlackPixel(f
->dpy
, screen
),
268 WhitePixel(f
->dpy
, screen
));
269 f
->gc
= XCreateGC(f
->dpy
, f
->w
, 0, 0);
270 XSelectInput(f
->dpy
, f
->w
,
271 ExposureMask
| KeyPressMask
| KeyReleaseMask
| ButtonPressMask
|
272 ButtonReleaseMask
| PointerMotionMask
);
273 XStoreName(f
->dpy
, f
->w
, f
->title
);
274 XMapWindow(f
->dpy
, f
->w
);
276 f
->img
= XCreateImage(f
->dpy
, DefaultVisual(f
->dpy
, 0), 24, ZPixmap
, 0,
277 (char *)f
->buf
, f
->width
, f
->height
, 32, 0);
280 FENSTER_API
void fenster_close(struct fenster
*f
) { XCloseDisplay(f
->dpy
); }
281 FENSTER_API
int fenster_loop(struct fenster
*f
) {
283 XPutImage(f
->dpy
, f
->w
, f
->gc
, f
->img
, 0, 0, 0, 0, f
->width
, f
->height
);
285 while (XPending(f
->dpy
)) {
286 XNextEvent(f
->dpy
, &ev
);
290 f
->mouse
= (ev
.type
== ButtonPress
);
293 f
->x
= ev
.xmotion
.x
, f
->y
= ev
.xmotion
.y
;
297 int m
= ev
.xkey
.state
;
298 int k
= XkbKeycodeToKeysym(f
->dpy
, ev
.xkey
.keycode
, 0, 0);
299 for (unsigned int i
= 0; i
< 124; i
+= 2) {
300 if (FENSTER_KEYCODES
[i
] == k
) {
301 f
->keys
[FENSTER_KEYCODES
[i
+ 1]] = (ev
.type
== KeyPress
);
305 f
->mod
= (!!(m
& ControlMask
)) | (!!(m
& ShiftMask
) << 1) |
306 (!!(m
& Mod1Mask
) << 2) | (!!(m
& Mod4Mask
) << 3);
315 FENSTER_API
void fenster_sleep(int64_t ms
) { Sleep(ms
); }
316 FENSTER_API
int64_t fenster_time() {
317 LARGE_INTEGER freq
, count
;
318 QueryPerformanceFrequency(&freq
);
319 QueryPerformanceCounter(&count
);
320 return (int64_t)(count
.QuadPart
* 1000.0 / freq
.QuadPart
);
323 FENSTER_API
void fenster_sleep(int64_t ms
) {
325 ts
.tv_sec
= ms
/ 1000;
326 ts
.tv_nsec
= (ms
% 1000) * 1000000;
327 nanosleep(&ts
, NULL
);
329 FENSTER_API
int64_t fenster_time(void) {
330 struct timespec time
;
331 clock_gettime(CLOCK_REALTIME
, &time
);
332 return time
.tv_sec
* 1000 + (time
.tv_nsec
/ 1000000);
342 Fenster(const int w
, const int h
, const char *title
)
343 : f
{.title
= title
, .width
= w
, .height
= h
} {
344 this->f
.buf
= new uint32_t[w
* h
];
346 this->now
= fenster_time();
347 fenster_open(&this->f
);
350 fenster_close(&this->f
);
351 delete[] this->f
.buf
;
353 void clear() { memset(this->f
.buf
, 0, this->f
.width
* this->f
.height
* 4); }
354 bool loop(const int fps
) {
355 int64_t t
= fenster_time();
356 int64_t paint_time
= t
- this->now
;
357 int64_t frame_budget
= 1000 / fps
;
358 int64_t sleep_time
= frame_budget
- paint_time
;
359 if (sleep_time
> 0) {
360 fenster_sleep(sleep_time
);
363 return fenster_loop(&this->f
) == 0;
365 void paint() { fenster_loop(&this->f
); }
366 inline uint32_t &px(const int x
, const int y
) {
367 return fenster_pixel(&this->f
, x
, y
);
369 bool key(int c
) { return c
>= 0 && c
< 128 ? this->f
.keys
[c
] : false; }
370 int x() { return this->f
.x
; }
371 int y() { return this->f
.y
; }
372 int mouse() { return this->f
.mouse
; }
373 int mod() { return this->f
.mod
; }
374 long width() { return this->f
.width
; }
375 long height() { return this->f
.height
; }
377 #endif /* __cplusplus */
379 #endif /* !FENSTER_HEADER */
380 #endif /* FENSTER_H */