Wuhoo  0.1
Windows using header only
wuhoo.h
Go to the documentation of this file.
1 
6 /* Platform Independent Section */
7 typedef void* WuhooHandle;
8 typedef void* WuhooResult;
9 typedef unsigned char WuhooBoolean;
10 typedef unsigned char WuhooByte;
11 typedef unsigned short WuhooR5G6B5;
12 typedef unsigned short WuhooR5G5B5;
13 typedef unsigned int WuhooSize;
14 
15 /* Macro helpers and utilities */
16 #ifdef __cplusplus
17 #define WuhooNull nullptr
18 #else
19 #define WuhooNull ((void*)0)
20 #endif
21 
22 #define WuhooSuccess WuhooNull
23 #define WuhooDefaultPosition (-2147483647 - 1) /* INT_MIN */
24 #define WuhooTrue (1)
25 #define WuhooInternal static
26 #define WuhooYes WuhooTrue
27 #define WuhooFalse (0)
28 #define WuhooNo WuhooFalse
29 #define WuhooUnused(x) (void)(x)
30 #define WuhooOnlySet(value, flag) (((flag) & (value)) == (flag))
31 #define WuhooFlag(shift_count) (1 << shift_count)
32 
33 #define WUHOO_STRING "Wuhoo"
34 #define WUHOO_MAX_FILE_NAME_LENGTH 256
35 
36 #ifdef __APPLE__
37 #define WUHOO_PLATFORM_API_STRING "Cocoa"
38 #endif
39 #ifdef _WIN32
40 #define WUHOO_PLATFORM_API_STRING "Win32"
41 #endif
42 #ifdef __linux__
43 #define WUHOO_PLATFORM_API_STRING "X11"
44 #endif
45 
46 /* Constraints */
47 #define WUHOO_MAX_TITLE_LENGTH 256
48 
49 typedef enum
50 {
51  WUHOO_FLAG_TITLED = WuhooFlag(0),
52 #ifdef WUHOO_OPENGL_ENABLE
53  WUHOO_FLAG_OPENGL = WuhooFlag(1),
54 #endif
55  WUHOO_FLAG_CANVAS = WuhooFlag(2),
56 #ifdef WUHOO_VULKAN_ENABLE
57  WUHOO_FLAG_VULKAN = WuhooFlag(3),
58 #endif
59  WUHOO_FLAG_RESIZEABLE = WuhooFlag(4),
60  WUHOO_FLAG_BORDERLESS = WuhooFlag(5),
61  WUHOO_FLAG_MOUSE_CAPTURE = WuhooFlag(6), /* Win32 for now */
62  WUHOO_FLAG_FILE_DROP = WuhooFlag(7),
63  WUHOO_FLAG_CLOSEABLE = WuhooFlag(8),
64  WUHOO_FLAG_CLIENT_REGION = WuhooFlag(9),
65  WUHOO_FLAG_WINDOW_REGION = WuhooFlag(10)
66 } WuhooFlagsEnum;
67 typedef unsigned int WuhooFlags;
68 
69 #ifdef WUHOO_OPENGL_ENABLE
70 typedef enum
71 {
72  WUHOO_GL_RED_SIZE,
73  WUHOO_GL_GREEN_SIZE,
74  WUHOO_GL_BLUE_SIZE,
75  WUHOO_GL_ALPHA_SIZE,
76  WUHOO_GL_BUFFER_SIZE,
77  WUHOO_GL_DOUBLEBUFFER,
78  WUHOO_GL_DEPTH_SIZE,
79  WUHOO_GL_STENCIL_SIZE,
80  WUHOO_GL_ACCUM_RED_SIZE,
81  WUHOO_GL_ACCUM_GREEN_SIZE,
82  WUHOO_GL_ACCUM_BLUE_SIZE,
83  WUHOO_GL_ACCUM_ALPHA_SIZE,
84  WUHOO_GL_STEREO,
85  WUHOO_GL_MULTISAMPLEBUFFERS,
86  WUHOO_GL_MULTISAMPLESAMPLES,
87  WUHOO_GL_ACCELERATED_VISUAL,
88  WUHOO_GL_RETAINED_BACKING,
89  WUHOO_GL_CONTEXT_MAJOR_VERSION,
90  WUHOO_GL_CONTEXT_MINOR_VERSION,
91  WUHOO_GL_CONTEXT_FLAGS,
92  WUHOO_GL_CONTEXT_PROFILE_MASK,
93  WUHOO_GL_SHARE_WITH_CURRENT_CONTEXT,
94  WUHOO_GL_FRAMEBUFFER_SRGB_CAPABLE,
95  WUHOO_GL_CONTEXT_RELEASE_BEHAVIOR,
96  WUHOO_GL_CONTEXT_EGL,
97 } WuhooGLAttrEnum;
98 #endif
99 typedef unsigned int WuhooGLAttr;
100 
101 typedef enum
102 {
103  WUHOO_KMOD_NONE = 0,
104  WUHOO_KMOD_LSHIFT = WuhooFlag(0),
105  WUHOO_KMOD_RSHIFT = WuhooFlag(1),
106  WUHOO_KMOD_LCTRL = WuhooFlag(2),
107  WUHOO_KMOD_RCTRL = WuhooFlag(3),
108  WUHOO_KMOD_LALT = WuhooFlag(4),
109  WUHOO_KMOD_RALT = WuhooFlag(5),
110  WUHOO_KMOD_LGUI = WuhooFlag(6),
111  WUHOO_KMOD_RGUI = WuhooFlag(7),
112  WUHOO_KMOD_NUM = WuhooFlag(8),
113  WUHOO_KMOD_CAPS = WuhooFlag(9),
114  WUHOO_KMOD_MODE = WuhooFlag(10),
115  WUHOO_KMOD_DEADCHAR = WuhooFlag(11),
116  WUHOO_KMOD_CTRL = (WUHOO_KMOD_LCTRL | WUHOO_KMOD_RCTRL),
117  WUHOO_KMOD_SHIFT = (WUHOO_KMOD_LSHIFT | WUHOO_KMOD_RSHIFT),
118  WUHOO_KMOD_ALT = (WUHOO_KMOD_LALT | WUHOO_KMOD_RALT),
119  WUHOO_KMOD_GUI = (WUHOO_KMOD_LGUI | WUHOO_KMOD_RGUI),
120  WUHOO_KMOD_MAX
121 } WuhooKeyModifiersEnum,
122  WuhooMouseModifiersEnum;
123 
124 typedef int WuhooMouseModifiers;
125 typedef int WuhooKeyModifiers;
126 
127 typedef enum
128 {
129  WUHOO_VKEY_UNKNOWN,
130 
131  WUHOO_VKEY_BACKSPACE = 8,
132  WUHOO_VKEY_FORWARD_DELETE = WUHOO_VKEY_BACKSPACE,
133  WUHOO_VKEY_TAB = 9,
134  WUHOO_VKEY_ENTER = 13,
135  WUHOO_VKEY_KPAD_ENTER,
136  WUHOO_VKEY_ESCAPE = 27,
137  WUHOO_VKEY_SPACE = ' ',
138 
139  WUHOO_VKEY_QUOTE = '\'',
140  WUHOO_VKEY_COMMA = ',',
141  WUHOO_VKEY_MINUS = '-',
142  WUHOO_VKEY_PERIOD = '.',
143  WUHOO_VKEY_FORWARD_SLASH = '/',
144 
145  WUHOO_VKEY_0 = '0',
146  WUHOO_VKEY_1,
147  WUHOO_VKEY_2,
148  WUHOO_VKEY_3,
149  WUHOO_VKEY_4,
150  WUHOO_VKEY_5,
151  WUHOO_VKEY_6,
152  WUHOO_VKEY_7,
153  WUHOO_VKEY_8,
154  WUHOO_VKEY_9 = '9',
155 
156  WUHOO_VKEY_SEMICOLON = ';',
157  WUHOO_VKEY_EQUALS = '=',
158 
159  WUHOO_VKEY_A = 'A',
160  WUHOO_VKEY_B,
161  WUHOO_VKEY_C,
162  WUHOO_VKEY_D,
163  WUHOO_VKEY_E,
164  WUHOO_VKEY_F,
165  WUHOO_VKEY_G,
166  WUHOO_VKEY_H,
167  WUHOO_VKEY_I,
168  WUHOO_VKEY_J,
169  WUHOO_VKEY_K,
170  WUHOO_VKEY_L,
171  WUHOO_VKEY_M,
172  WUHOO_VKEY_N,
173  WUHOO_VKEY_O,
174  WUHOO_VKEY_P,
175  WUHOO_VKEY_Q,
176  WUHOO_VKEY_R,
177  WUHOO_VKEY_S,
178  WUHOO_VKEY_T,
179  WUHOO_VKEY_U,
180  WUHOO_VKEY_V,
181  WUHOO_VKEY_W,
182  WUHOO_VKEY_X,
183  WUHOO_VKEY_Y,
184  WUHOO_VKEY_Z = 'Z',
185 
186  WUHOO_VKEY_LEFT_BRACKET = '[',
187  WUHOO_VKEY_BACK_SLASH = '\\',
188  WUHOO_VKEY_RIGHT_BRACKET = ']',
189 
190  WUHOO_VKEY_GRAVE = '`',
191  WUHOO_VKEY_TILDA = WUHOO_VKEY_GRAVE,
192  WUHOO_VKEY_DELETE = 127,
193 
194  WUHOO_VKEY_F1,
195  WUHOO_VKEY_F2,
196  WUHOO_VKEY_F3,
197  WUHOO_VKEY_F4,
198  WUHOO_VKEY_F5,
199  WUHOO_VKEY_F6,
200  WUHOO_VKEY_F7,
201  WUHOO_VKEY_F8,
202  WUHOO_VKEY_F9,
203  WUHOO_VKEY_F10,
204  WUHOO_VKEY_F11,
205  WUHOO_VKEY_F12,
206  WUHOO_VKEY_F13,
207  WUHOO_VKEY_PRINTSCREEN = WUHOO_VKEY_F13,
208  WUHOO_VKEY_F14,
209  WUHOO_VKEY_F15,
210 
211  WUHOO_VKEY_INSERT,
212  WUHOO_VKEY_HELP = WUHOO_VKEY_INSERT,
213  WUHOO_VKEY_HOME,
214  WUHOO_VKEY_PAGE_UP,
215  WUHOO_VKEY_END,
216  WUHOO_VKEY_PAGE_DOWN,
217  WUHOO_VKEY_MENU,
218 
219  WUHOO_VKEY_KPAD_0,
220  WUHOO_VKEY_KPAD_1,
221  WUHOO_VKEY_KPAD_2,
222  WUHOO_VKEY_KPAD_3,
223  WUHOO_VKEY_KPAD_4,
224  WUHOO_VKEY_KPAD_5,
225  WUHOO_VKEY_KPAD_6,
226  WUHOO_VKEY_KPAD_7,
227  WUHOO_VKEY_KPAD_8,
228  WUHOO_VKEY_KPAD_9,
229 
230  WUHOO_VKEY_KPAD_DIVIDE,
231  WUHOO_VKEY_KPAD_SLASH = WUHOO_VKEY_KPAD_DIVIDE,
232  WUHOO_VKEY_KPAD_PLUS,
233  WUHOO_VKEY_KPAD_MINUS,
234  WUHOO_VKEY_KPAD_EQUALS,
235  WUHOO_VKEY_KPAD_MULITPLY,
236  WUHOO_VKEY_KPAD_DECIMAL,
237  WUHOO_VKEY_KPAD_NUM_LOCK,
238  WUHOO_VKEY_KPAD_COMMA = WUHOO_VKEY_KPAD_DECIMAL,
239 
240  WUHOO_VKEY_SHIFT,
241  WUHOO_VKEY_CONTROL,
242  WUHOO_VKEY_ALT,
243  WUHOO_VKEY_CAPS_LOCK,
244  WUHOO_VKEY_UP,
245  WUHOO_VKEY_DOWN,
246  WUHOO_VKEY_RIGHT,
247  WUHOO_VKEY_LEFT,
248 
249  WUHOO_VKEY_MAX
250 } WuhooKeyCode;
251 
255 typedef enum {
256  WUHOO_WINDOW_FLAG_RESIZED = WuhooFlag(0),
257  WUHOO_WINDOW_FLAG_FULL_SCREEN = WuhooFlag(1),
258  WUHOO_WINDOW_FLAG_FOCUS_GAINED = WuhooFlag(2),
259  WUHOO_WINDOW_FLAG_FOCUS_LOST = WuhooFlag(3),
260  WUHOO_WINDOW_FLAG_MINIMIZED = WuhooFlag(4),
261  WUHOO_WINDOW_FLAG_MAXIMIZED = WuhooFlag(5),
262  WUHOO_WINDOW_FLAG_MOVED = WuhooFlag(6),
263  WUHOO_WINDOW_FLAG_CLOSED = WuhooFlag(7),
264  WUHOO_WINDOW_FLAG_REGION_UPDATED = WuhooFlag(8),
265  WUHOO_WINDOW_FLAG_DROP_STARTED = WuhooFlag(9)
266 } WuhooWindowFlagsEnum;
267 
268 typedef int WuhooWindowFlags;
269 
273 typedef enum {
274  WUHOO_WSTATE_UNKNOWN,
275  WUHOO_WSTATE_RESIZED,
276  WUHOO_WSTATE_FULL_SCREENED,
277  WUHOO_WSTATE_MOVED,
278  WUHOO_WSTATE_MINIMIZED,
279  WUHOO_WSTATE_MAXIMIZED,
280  WUHOO_WSTATE_CREATED,
281  WUHOO_WSTATE_CLOSED,
282  WUHOO_WSTATE_UNFOCUSED,
283  WUHOO_WSTATE_FOCUSED,
284  WUHOO_WSTATE_HIDDEN,
285  WUHOO_WSTATE_INVALIDATED,
286  WUHOO_WSTATE_MAX,
288 
292 typedef enum {
293  WUHOO_EVT_NONE,
300  WUHOO_EVT_MAX
302 
303 typedef enum {
304  WUHOO_KSTATE_UNKNOWN,
305  WUHOO_KSTATE_UP,
306  WUHOO_KSTATE_DOWN,
307  WUHOO_KSTATE_MAX
308 } WuhooKeyState;
309 
310 typedef unsigned int WuhooUTF32; /* at least 32 bits */
311 typedef unsigned short WuhooUTF16; /* at least 16 bits */
312 typedef unsigned char WuhooUTF8; /* typically 8 bits */
313 
314 #define WUHOO_MAX_CHARACTER_SIZE 6
315 
319 typedef struct
320 {
321  WuhooKeyModifiers mods;
322  WuhooKeyState state;
323  WuhooKeyCode code;
324  WuhooUTF8 character[WUHOO_MAX_CHARACTER_SIZE]; /* UTF-8 representation of the submitted character */
325 } WuhooEventKey;
326 
330 typedef enum {
331  WUHOO_MSTATE_UNKNOWN,
332  WUHOO_MSTATE_LPRESSED,
333  WUHOO_MSTATE_LRELEASED,
334  WUHOO_MSTATE_RPRESSED,
335  WUHOO_MSTATE_RRELEASED,
336  WUHOO_MSTATE_MPRESSED,
337  WUHOO_MSTATE_MRELEASED,
338  WUHOO_MSTATE_MAX
340 
346 typedef struct
347 {
348  unsigned char r;
349  unsigned char g;
350  unsigned char b;
351  unsigned char a;
352 } WuhooRGBA;
353 
354 typedef struct
355 {
356  WuhooMouseModifiers mods;
357  WuhooMouseState state;
358  int x;
359  int y;
361 
362 typedef struct
363 {
364  WuhooMouseModifiers mods;
365  WuhooMouseState state;
366  int click_count;
367  int x;
368  int y;
370 
371 typedef struct
372 {
373  WuhooMouseModifiers mods;
374  int x;
375  int y;
376  float delta_x;
377  float delta_y;
379 
380 typedef struct
381 {
382  WuhooWindowState state;
383  WuhooWindowFlags flags;
384  int data1;
385  int data2;
387 
388 typedef struct
389 {
390  WuhooHandle context;
391  WuhooSize count;
392  WuhooSize size;
394 
395 typedef union
396 {
397  WuhooEventKey key;
398  WuhooEventMousePress mouse_press;
399  WuhooEventMouseMove mouse_move;
400  WuhooEventMouseWheel mouse_wheel;
401  WuhooEventWindow window;
402  WuhooEventDrop drop;
404 
408 typedef struct
409 {
410  WuhooEventType type;
411  WuhooEventData data;
412 } WuhooEvent;
413 
414 typedef WuhooResult (*WuhooConvertRGBA)(void* dst, WuhooRGBA const* const src,
415  WuhooSize x, WuhooSize y,
416  WuhooSize width, WuhooSize height,
417  WuhooSize src_width,
418  WuhooSize src_height);
419 
420 #ifdef WUHOO_OPENGL_ENABLE
421 typedef struct
422 {
423  int major;
424  int minor;
425 } WuhooGLVersion;
426 
427 typedef struct
428 {
429  WuhooGLVersion version;
430  int redBits;
431  int greenBits;
432  int blueBits;
433  int alphaBits;
434  int depthBits;
435  int stencilBits;
436  int accumRedBits;
437  int accumGreenBits;
438  int accumBlueBits;
439  int accumAlphaBits;
440  int auxBuffers;
441  int samples;
442  WuhooBoolean stereo;
443  WuhooBoolean sRGB;
444  WuhooBoolean doublebuffer;
445  WuhooBoolean transparent;
446 } WuhooGLFramebuffer;
447 #endif /* WUHOO_OPENGL_ENABLE */
448 
449 typedef struct
450 {
451 #ifdef WUHOO_OPENGL_ENABLE
452  WuhooGLFramebuffer gl_framebuffer;
453 #endif
454  WuhooHandle platform_window;
455  WuhooConvertRGBA convert_rgba;
456  int window_flags;
457  WuhooFlags flags;
458  int global_mods;
459  int width; /* window width */
460  int height; /* window height */
461  int cwidth; /* client width */
462  int cheight; /* client height */
463  int x;
464  int y;
465  WuhooBoolean is_initialized;
466  WuhooBoolean is_alive;
467  WuhooByte memory[256];
468 } WuhooWindow;
469 
470 /* Public API */
471 
477 WuhooResult
484 WuhooResult
499 WuhooResult
500 WuhooWindowCreate(WuhooWindow* window, int posx, int posy, WuhooSize width,
501  WuhooSize height, const char* title, WuhooFlags flags,
502  const void* data);
509 WuhooResult
521 WuhooResult
522 WuhooWindowDropContentsGet(WuhooWindow* window, WuhooEvent* event, char* buffer,
523  WuhooSize buffer_size);
534 WuhooResult
535 WuhooWindowRegionSet(WuhooWindow* window, int posx, int posy, WuhooSize width,
536  WuhooSize height);
547 WuhooResult
548 WuhooWindowRegionGet(WuhooWindow* window, int* posx, int* posy,
549  WuhooSize* width, WuhooSize* height);
560 WuhooResult
561 WuhooWindowClientRegionGet(WuhooWindow* window, int* posx, int* posy,
562  WuhooSize* width, WuhooSize* height);
573 WuhooResult
574 WuhooWindowClientRegionSet(WuhooWindow* window, int posx, int posy,
575  WuhooSize width, WuhooSize height);
597 WuhooResult
598 WuhooWindowBlit(WuhooWindow* window, WuhooRGBA* pixels, WuhooSize src_x,
599  WuhooSize src_y, WuhooSize src_width, WuhooSize src_height,
600  WuhooSize dst_x, WuhooSize dst_y, WuhooSize dst_width,
601  WuhooSize dst_height);
606 WuhooResult
608 
616 WuhooResult
617 WuhooWindowSetTitle(WuhooWindow* window, const char* title);
623 const char*
624 WuhooResultString(WuhooResult result);
625 
626 #ifdef WUHOO_IMPLEMENTATION
627 
628 WuhooInternal void
629 WuhooCopy(void* const to, void const* const from, WuhooSize count);
630 WuhooInternal WuhooBoolean
631 WuhooStringCmp(const char* to, const char* from, WuhooSize max_count);
632 /* Color conversion kernels */
633 WuhooInternal WuhooResult
634 WuhooConvertRGBANoOp(void* dst, WuhooRGBA const* const src, WuhooSize x,
635  WuhooSize y, WuhooSize width, WuhooSize height,
636  WuhooSize src_width, WuhooSize src_height);
637 WuhooInternal WuhooResult
638 WuhooConvertRGBAtoRGBA(void* dst, WuhooRGBA const* const src, WuhooSize x,
639  WuhooSize y, WuhooSize width, WuhooSize height,
640  WuhooSize src_width, WuhooSize src_height);
641 WuhooInternal WuhooResult
642 WuhooConvertRGBAtoRGB(void* dst, WuhooRGBA const* const src, WuhooSize x,
643  WuhooSize y, WuhooSize width, WuhooSize height,
644  WuhooSize src_width, WuhooSize src_height);
645 WuhooInternal WuhooResult
646 WuhooConvertRGBAtoBGRA(void* dst, WuhooRGBA const* const src, WuhooSize x,
647  WuhooSize y, WuhooSize width, WuhooSize height,
648  WuhooSize src_width, WuhooSize src_height);
649 WuhooInternal WuhooResult
650 WuhooConvertRGBAtoR5G6B5(void* dst, WuhooRGBA const* const src, WuhooSize x,
651  WuhooSize y, WuhooSize width, WuhooSize height,
652  WuhooSize src_width, WuhooSize src_height);
653 
654 /* Helpers and utilities */
655 WuhooInternal void
656 WuhooCharacterCopy(char* to, const char* from);
657 WuhooInternal void
658 WuhooZeroInit(void* to, WuhooSize count);
659 WuhooInternal WuhooSize
660 WuhooStringCopy(char* to, const char* from, WuhooSize max_count);
661 WuhooInternal int
662 WuhooMini(int a, int b);
663 WuhooInternal int
664 WuhooMaxi(int a, int b);
665 WuhooInternal void
666 WuhooMemzero(void* address, WuhooSize size);
667 WuhooInternal WuhooSize
668 WuhooStringLength(const char* str, WuhooSize max_count);
669 #ifdef UNICODE
670 #ifndef WUHOO_UNICODE
671 #define WUHOO_UNICODE
672 #endif
673 #endif
674 
675 #ifdef WUHOO_UNICODE
676 #ifndef UNICODE
677 #define UNICODE
678 #endif
679 #else
680 #define WUHOO_ANSI
681 #endif
682 
683 #if defined WUHOO_UNICODE && defined _WIN32
684 typedef WuhooUTF16 WuhooChar;
685 #else
686 typedef WuhooUTF8 WuhooChar;
687 #endif
688 
689 /* Some fundamental constants */
690 #define WUHOO_UNI_REPLACEMENT_CHAR (WuhooUTF32)0x0000FFFD
691 #define WUHOO_UNI_MAX_BMP (WuhooUTF32)0x0000FFFF
692 #define WUHOO_UNI_MAX_UTF16 (WuhooUTF32)0x0010FFFF
693 #define WUHOO_UNI_MAX_UTF32 (WuhooUTF32)0x7FFFFFFF
694 #define WUHOO_UNI_MAX_LEGAL_UTF32 (WuhooUTF32)0x0010FFFF
695 
696 #define WUHOO_UNI_MAX_UTF8_BYTES_PER_CODE_POINT 4
697 
698 #define WUHOO_UNI_UTF16_BYTE_ORDER_MARK_NATIVE 0xFEFF
699 #define WUHOO_UNI_UTF16_BYTE_ORDER_MARK_SWAPPED 0xFFFE
700 
701 typedef enum
702  {
703  WuhooConversionOK, /* conversion successful */
704  WuhooSourceExhausted, /* partial character in source, but hit end */
705  WuhooTargetExhausted, /* insuff. room in target for conversion */
706  WuhooSourceIllegal /* source sequence is illegal/malformed */
707  } WuhooConversionResult;
708 
709 typedef enum
710  {
711  WuhooStrictConversion = 0,
712  WuhooLenientConversion
713  } WuhooConversionFlags;
714 
715 WuhooConversionResult
716 WuhooConvertUTF8toUTF16(const WuhooUTF8** sourceStart,
717  const WuhooUTF8* sourceEnd, WuhooUTF16** targetStart,
718  WuhooUTF16* targetEnd, WuhooConversionFlags flags);
719 
724 WuhooConversionResult
725 ConvertUTF8toUTF32Partial(const WuhooUTF8** sourceStart,
726  const WuhooUTF8* sourceEnd, WuhooUTF32** targetStart,
727  WuhooUTF32* targetEnd, WuhooConversionFlags flags);
728 
733 WuhooConversionResult
734 WuhooConvertUTF8toUTF32(const WuhooUTF8** sourceStart,
735  const WuhooUTF8* sourceEnd, WuhooUTF32** targetStart,
736  WuhooUTF32* targetEnd, WuhooConversionFlags flags);
737 
738 WuhooConversionResult
739 WuhooConvertUTF16toUTF8(const WuhooUTF16** sourceStart,
740  const WuhooUTF16* sourceEnd, WuhooUTF8** targetStart,
741  WuhooUTF8* targetEnd, WuhooConversionFlags flags);
742 
743 WuhooConversionResult
744 WuhooConvertUTF32toUTF8(const WuhooUTF32** sourceStart,
745  const WuhooUTF32* sourceEnd, WuhooUTF8** targetStart,
746  WuhooUTF8* targetEnd, WuhooConversionFlags flags);
747 
748 WuhooConversionResult
749 ConvertUTF16toUTF32(const WuhooUTF16** sourceStart, const WuhooUTF16* sourceEnd,
750  WuhooUTF32** targetStart, WuhooUTF32* targetEnd,
751  WuhooConversionFlags flags);
752 
753 WuhooConversionResult
754 WuhooConvertUTF32toUTF16(const WuhooUTF32** sourceStart,
755  const WuhooUTF32* sourceEnd, WuhooUTF16** targetStart,
756  WuhooUTF16* targetEnd, WuhooConversionFlags flags);
757 
758 WuhooBoolean
759 WuhooIsLegalUTF8Sequence(const WuhooUTF8* source, const WuhooUTF8* sourceEnd);
760 
761 WuhooBoolean
762 WuhooIsLegalUTF8String(const WuhooUTF8** source, const WuhooUTF8* sourceEnd);
763 
764 unsigned
765 WuhooGetNumBytesForUTF8(WuhooUTF8 firstByte);
766 
767 static const int WuhooHalfShift = 10; /* used for shifting by 10 bits */
768 
769 static const WuhooUTF32 WuhooHalfBase = 0x0010000UL;
770 static const WuhooUTF32 WuhooHalfMask = 0x3FFUL;
771 
772 #define WUHOO_UNI_SUR_HIGH_START (WuhooUTF32)0xD800
773 #define WUHOO_UNI_SUR_HIGH_END (WuhooUTF32)0xDBFF
774 #define WUHOO_UNI_SUR_LOW_START (WuhooUTF32)0xDC00
775 #define WUHOO_UNI_SUR_LOW_END (WuhooUTF32)0xDFFF
776 
777 /* --------------------------------------------------------------------- */
778 
779 /*
780  * Index into the table below with the first byte of a UTF-8 sequence to
781  * get the number of trailing bytes that are supposed to follow it.
782  * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
783  * left as-is for anyone who may want to do such conversion, which was
784  * allowed in earlier algorithms.
785  */
786 static const char WuhooTrailingBytesForUTF8[256] = {
787  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
788  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
789  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
790  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
791  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
792  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
793  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
794  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
795  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
796  2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5
797 };
798 
799 /*
800  * Magic values subtracted from a buffer value during UTF8 conversion.
801  * This table contains as many values as there might be trailing bytes
802  * in a UTF-8 sequence.
803  */
804 static const WuhooUTF32 WuhooOffsetsFromUTF8[6] = {
805  0x00000000UL, 0x00003080UL, 0x000E2080UL,
806  0x03C82080UL, 0xFA082080UL, 0x82082080UL
807 };
808 
809 /*
810  * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
811  * into the first byte, depending on how many bytes follow. There are
812  * as many entries in this table as there are UTF-8 sequence types.
813  * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
814  * for *legal* UTF-8 will be 4 or fewer bytes total.
815  */
816 static const WuhooUTF8 WuhooFirstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0,
817  0xF0, 0xF8, 0xFC };
818 
819 /*
820  * Utility routine to tell whether a sequence of bytes is legal UTF-8.
821  * This must be called with the length pre-determined by the first byte.
822  * If not calling this from ConvertUTF8to*, then the length can be set by:
823  * length = trailingBytesForUTF8[*source]+1;
824  * and the sequence is illegal right away if there aren't that many bytes
825  * available.
826  * If presented with a length > 4, this returns false. The Unicode
827  * definition of UTF-8 goes up to 4-byte sequences.
828  */
829 
830 static WuhooBoolean
831 WuhooIsLegalUTF8(const WuhooUTF8* source, int length)
832 {
833  WuhooUTF8 a;
834  const WuhooUTF8* srcptr = source + length;
835  switch (length) {
836  default:
837  return WuhooFalse;
838  /* Everything else falls through when "true"... */
839  case 4:
840  if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
841  return WuhooFalse;
842  case 3:
843  if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
844  return WuhooFalse;
845  case 2:
846  if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
847  return WuhooFalse;
848 
849  switch (*source) {
850  /* no fall-through in this inner switch */
851  case 0xE0:
852  if (a < 0xA0)
853  return WuhooFalse;
854  break;
855  case 0xED:
856  if (a > 0x9F)
857  return WuhooFalse;
858  break;
859  case 0xF0:
860  if (a < 0x90)
861  return WuhooFalse;
862  break;
863  case 0xF4:
864  if (a > 0x8F)
865  return WuhooFalse;
866  break;
867  default:
868  if (a < 0x80)
869  return WuhooFalse;
870  }
871 
872  case 1:
873  if (*source >= 0x80 && *source < 0xC2)
874  return WuhooFalse;
875  }
876  if (*source > 0xF4)
877  return WuhooFalse;
878  return WuhooTrue;
879 }
880 
881 /* --------------------------------------------------------------------- */
882 
883 /*
884  * Exported function to return whether a UTF-8 sequence is legal or not.
885  * This is not used here; it's just exported.
886  */
887 WuhooBoolean
888 WuhooIsLegalUTF8Sequence(const WuhooUTF8* source, const WuhooUTF8* sourceEnd)
889 {
890  int length = WuhooTrailingBytesForUTF8[*source] + 1;
891  if (length > sourceEnd - source) {
892  return WuhooFalse;
893  }
894  return WuhooIsLegalUTF8(source, length);
895 }
896 
897 /* --------------------------------------------------------------------- */
898 
899 /* The interface converts a whole buffer to avoid function-call overhead.
900  * Constants have been gathered. Loops & conditionals have been removed as
901  * much as possible for efficiency, in favor of drop-through switches.
902  * (See "Note A" at the bottom of the file for equivalent code.)
903  * If your compiler supports it, the "isLegalUTF8" call can be turned
904  * into an inline function.
905  */
906 
907 WuhooConversionResult
908 WuhooConvertUTF8toUTF16(const WuhooUTF8** sourceStart,
909  const WuhooUTF8* sourceEnd, WuhooUTF16** targetStart,
910  WuhooUTF16* targetEnd, WuhooConversionFlags flags)
911 {
912  WuhooConversionResult result = WuhooConversionOK;
913  const WuhooUTF8* source = *sourceStart;
914  WuhooUTF16* target = *targetStart;
915  while (source < sourceEnd) {
916  WuhooUTF32 ch = 0;
917  unsigned short extraBytesToRead = WuhooTrailingBytesForUTF8[*source];
918  if (extraBytesToRead >= sourceEnd - source) {
919  result = WuhooSourceExhausted;
920  break;
921  }
922  /* Do this check whether lenient or strict */
923  if (!WuhooIsLegalUTF8(source, extraBytesToRead + 1)) {
924  result = WuhooSourceIllegal;
925  break;
926  }
927  /*
928  * The cases all fall through. See "Note A" below.
929  */
930  switch (extraBytesToRead) {
931  case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
932  case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
933  case 3: ch += *source++; ch <<= 6;
934  case 2: ch += *source++; ch <<= 6;
935  case 1: ch += *source++; ch <<= 6;
936  case 0: ch += *source++;
937  }
938  ch -= WuhooOffsetsFromUTF8[extraBytesToRead];
939 
940  if (target >= targetEnd) {
941  source -= (extraBytesToRead + 1); /* Back up source pointer! */
942  result = WuhooTargetExhausted;
943  break;
944  }
945  if (ch <= WUHOO_UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
946  /* UTF-16 surrogate values are illegal in UTF-32 */
947  if (ch >= WUHOO_UNI_SUR_HIGH_START && ch <= WUHOO_UNI_SUR_LOW_END) {
948  if (flags == WuhooStrictConversion) {
949  source -=
950  (extraBytesToRead + 1); /* return to the illegal value itself */
951  result = WuhooSourceIllegal;
952  break;
953  } else {
954  *target++ = WUHOO_UNI_REPLACEMENT_CHAR;
955  }
956  } else {
957  *target++ = (WuhooUTF16)ch; /* normal case */
958  }
959  } else if (ch > WUHOO_UNI_MAX_UTF16) {
960  if (flags == WuhooStrictConversion) {
961  result = WuhooSourceIllegal;
962  source -= (extraBytesToRead + 1); /* return to the start */
963  break; /* Bail out; shouldn't continue */
964  } else {
965  *target++ = WUHOO_UNI_REPLACEMENT_CHAR;
966  }
967  } else {
968  /* target is a character in range 0xFFFF - 0x10FFFF. */
969  if (target + 1 >= targetEnd) {
970  source -= (extraBytesToRead + 1); /* Back up source pointer! */
971  result = WuhooTargetExhausted;
972  break;
973  }
974  ch -= WuhooHalfBase;
975  *target++ =
976  (WuhooUTF16)((ch >> WuhooHalfShift) + WUHOO_UNI_SUR_HIGH_START);
977  *target++ = (WuhooUTF16)((ch & WuhooHalfMask) + WUHOO_UNI_SUR_LOW_START);
978  }
979  }
980  *sourceStart = source;
981  *targetStart = target;
982  return result;
983 }
984 
985 static unsigned
986 WuhooFindMaximalSubpartOfIllFormedUTF8Sequence(const WuhooUTF8* source,
987  const WuhooUTF8* sourceEnd)
988 {
989  WuhooUTF8 b1, b2, b3;
990 
991  // assert(!isLegalUTF8Sequence(source, sourceEnd));
992 
993  /*
994  * Unicode 6.3.0, D93b:
995  *
996  * Maximal subpart of an ill-formed subsequence: The longest code unit
997  * subsequence starting at an unconvertible offset that is either:
998  * a. the initial subsequence of a well-formed code unit sequence, or
999  * b. a subsequence of length one.
1000  */
1001 
1002  if (source == sourceEnd)
1003  return 0;
1004 
1005  /*
1006  * Perform case analysis. See Unicode 6.3.0, Table 3-7. Well-Formed UTF-8
1007  * Byte Sequences.
1008  */
1009 
1010  b1 = *source;
1011  ++source;
1012  if (b1 >= 0xC2 && b1 <= 0xDF) {
1013  /*
1014  * First byte is valid, but we know that this code unit sequence is
1015  * invalid, so the maximal subpart has to end after the first byte.
1016  */
1017  return 1;
1018  }
1019 
1020  if (source == sourceEnd)
1021  return 1;
1022 
1023  b2 = *source;
1024  ++source;
1025 
1026  if (b1 == 0xE0) {
1027  return (b2 >= 0xA0 && b2 <= 0xBF) ? 2 : 1;
1028  }
1029  if (b1 >= 0xE1 && b1 <= 0xEC) {
1030  return (b2 >= 0x80 && b2 <= 0xBF) ? 2 : 1;
1031  }
1032  if (b1 == 0xED) {
1033  return (b2 >= 0x80 && b2 <= 0x9F) ? 2 : 1;
1034  }
1035  if (b1 >= 0xEE && b1 <= 0xEF) {
1036  return (b2 >= 0x80 && b2 <= 0xBF) ? 2 : 1;
1037  }
1038  if (b1 == 0xF0) {
1039  if (b2 >= 0x90 && b2 <= 0xBF) {
1040  if (source == sourceEnd)
1041  return 2;
1042 
1043  b3 = *source;
1044  return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
1045  }
1046  return 1;
1047  }
1048  if (b1 >= 0xF1 && b1 <= 0xF3) {
1049  if (b2 >= 0x80 && b2 <= 0xBF) {
1050  if (source == sourceEnd)
1051  return 2;
1052 
1053  b3 = *source;
1054  return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
1055  }
1056  return 1;
1057  }
1058  if (b1 == 0xF4) {
1059  if (b2 >= 0x80 && b2 <= 0x8F) {
1060  if (source == sourceEnd)
1061  return 2;
1062 
1063  b3 = *source;
1064  return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
1065  }
1066  return 1;
1067  }
1068 
1069  // assert((b1 >= 0x80 && b1 <= 0xC1) || b1 >= 0xF5);
1070  /*
1071  * There are no valid sequences that start with these bytes. Maximal subpart
1072  * is defined to have length 1 in these cases.
1073  */
1074  return 1;
1075 }
1076 
1077 static WuhooConversionResult
1078 WuhooConvertUTF8toUTF32Impl(const WuhooUTF8** sourceStart,
1079  const WuhooUTF8* sourceEnd,
1080  WuhooUTF32** targetStart, WuhooUTF32* targetEnd,
1081  WuhooConversionFlags flags,
1082  WuhooBoolean InputIsPartial)
1083 {
1084  WuhooConversionResult result = WuhooConversionOK;
1085  const WuhooUTF8* source = *sourceStart;
1086  WuhooUTF32* target = *targetStart;
1087  while (source < sourceEnd) {
1088  WuhooUTF32 ch = 0;
1089  unsigned short extraBytesToRead = WuhooTrailingBytesForUTF8[*source];
1090  if (extraBytesToRead >= sourceEnd - source) {
1091  if (flags == WuhooStrictConversion || InputIsPartial) {
1092  result = WuhooSourceExhausted;
1093  break;
1094  } else {
1095  result = WuhooSourceIllegal;
1096 
1097  /*
1098  * Replace the maximal subpart of ill-formed sequence with
1099  * replacement character.
1100  */
1101  source +=
1102  WuhooFindMaximalSubpartOfIllFormedUTF8Sequence(source, sourceEnd);
1103  *target++ = WUHOO_UNI_REPLACEMENT_CHAR;
1104  continue;
1105  }
1106  }
1107  if (target >= targetEnd) {
1108  result = WuhooTargetExhausted;
1109  break;
1110  }
1111 
1112  /* Do this check whether lenient or strict */
1113  if (!WuhooIsLegalUTF8(source, extraBytesToRead + 1)) {
1114  result = WuhooSourceIllegal;
1115  if (flags == WuhooStrictConversion) {
1116  /* Abort conversion. */
1117  break;
1118  } else {
1119  /*
1120  * Replace the maximal subpart of ill-formed sequence with
1121  * replacement character.
1122  */
1123  source +=
1124  WuhooFindMaximalSubpartOfIllFormedUTF8Sequence(source, sourceEnd);
1125  *target++ = WUHOO_UNI_REPLACEMENT_CHAR;
1126  continue;
1127  }
1128  }
1129  /*
1130  * The cases all fall through. See "Note A" below.
1131  */
1132  switch (extraBytesToRead) {
1133  case 5: ch += *source++; ch <<= 6;
1134  case 4: ch += *source++; ch <<= 6;
1135  case 3: ch += *source++; ch <<= 6;
1136  case 2: ch += *source++; ch <<= 6;
1137  case 1: ch += *source++; ch <<= 6;
1138  case 0: ch += *source++;
1139  }
1140  ch -= WuhooOffsetsFromUTF8[extraBytesToRead];
1141 
1142  if (ch <= WUHOO_UNI_MAX_LEGAL_UTF32) {
1143  /*
1144  * UTF-16 surrogate values are illegal in UTF-32, and anything
1145  * over Plane 17 (> 0x10FFFF) is illegal.
1146  */
1147  if (ch >= WUHOO_UNI_SUR_HIGH_START && ch <= WUHOO_UNI_SUR_LOW_END) {
1148  if (flags == WuhooStrictConversion) {
1149  source -=
1150  (extraBytesToRead + 1); /* return to the illegal value itself */
1151  result = WuhooSourceIllegal;
1152  break;
1153  } else {
1154  *target++ = WUHOO_UNI_REPLACEMENT_CHAR;
1155  }
1156  } else {
1157  *target++ = ch;
1158  }
1159  } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
1160  result = WuhooSourceIllegal;
1161  *target++ = WUHOO_UNI_REPLACEMENT_CHAR;
1162  }
1163  }
1164  *sourceStart = source;
1165  *targetStart = target;
1166  return result;
1167 }
1168 
1169 WuhooConversionResult
1170 WuhooConvertUTF8toUTF32Partial(const WuhooUTF8** sourceStart,
1171  const WuhooUTF8* sourceEnd,
1172  WuhooUTF32** targetStart, WuhooUTF32* targetEnd,
1173  WuhooConversionFlags flags)
1174 {
1175  return WuhooConvertUTF8toUTF32Impl(sourceStart, sourceEnd, targetStart,
1176  targetEnd, flags,
1177  /*InputIsPartial=*/WuhooTrue);
1178 }
1179 
1180 WuhooConversionResult
1181 WuhooConvertUTF8toUTF32(const WuhooUTF8** sourceStart,
1182  const WuhooUTF8* sourceEnd, WuhooUTF32** targetStart,
1183  WuhooUTF32* targetEnd, WuhooConversionFlags flags)
1184 {
1185  return WuhooConvertUTF8toUTF32Impl(sourceStart, sourceEnd, targetStart,
1186  targetEnd, flags,
1187  /*InputIsPartial=*/WuhooFalse);
1188 }
1189 
1190 WuhooConversionResult
1191 WuhooConvertUTF16toUTF8(const WuhooUTF16** sourceStart,
1192  const WuhooUTF16* sourceEnd, WuhooUTF8** targetStart,
1193  WuhooUTF8* targetEnd, WuhooConversionFlags flags)
1194 {
1195  WuhooConversionResult result = WuhooConversionOK;
1196  const WuhooUTF16* source = *sourceStart;
1197  WuhooUTF8* target = *targetStart;
1198  while (source < sourceEnd) {
1199  WuhooUTF32 ch;
1200  unsigned short bytesToWrite = 0;
1201  const WuhooUTF32 byteMask = 0xBF;
1202  const WuhooUTF32 byteMark = 0x80;
1203  const WuhooUTF16* oldSource =
1204  source; /* In case we have to back up because of target overflow. */
1205  ch = *source++;
1206  /* If we have a surrogate pair, convert to UTF32 first. */
1207  if (ch >= WUHOO_UNI_SUR_HIGH_START && ch <= WUHOO_UNI_SUR_HIGH_END) {
1208  /* If the 16 bits following the high surrogate are in the source buffer...
1209  */
1210  if (source < sourceEnd) {
1211  WuhooUTF32 ch2 = *source;
1212  /* If it's a low surrogate, convert to UTF32. */
1213  if (ch2 >= WUHOO_UNI_SUR_LOW_START && ch2 <= WUHOO_UNI_SUR_LOW_END) {
1214  ch = ((ch - WUHOO_UNI_SUR_HIGH_START) << WuhooHalfShift) +
1215  (ch2 - WUHOO_UNI_SUR_LOW_START) + WuhooHalfBase;
1216  ++source;
1217  } else if (flags == WuhooStrictConversion) { /* it's an unpaired high
1218  surrogate */
1219  --source; /* return to the illegal value itself */
1220  result = WuhooSourceIllegal;
1221  break;
1222  }
1223  } else { /* We don't have the 16 bits following the high surrogate. */
1224  --source; /* return to the high surrogate */
1225  result = WuhooSourceExhausted;
1226  break;
1227  }
1228  } else if (flags == WuhooStrictConversion) {
1229  /* UTF-16 surrogate values are illegal in UTF-32 */
1230  if (ch >= WUHOO_UNI_SUR_LOW_START && ch <= WUHOO_UNI_SUR_LOW_END) {
1231  --source; /* return to the illegal value itself */
1232  result = WuhooSourceIllegal;
1233  break;
1234  }
1235  }
1236  /* Figure out how many bytes the result will require */
1237  if (ch < (WuhooUTF32)0x80) {
1238  bytesToWrite = 1;
1239  } else if (ch < (WuhooUTF32)0x800) {
1240  bytesToWrite = 2;
1241  } else if (ch < (WuhooUTF32)0x10000) {
1242  bytesToWrite = 3;
1243  } else if (ch < (WuhooUTF32)0x110000) {
1244  bytesToWrite = 4;
1245  } else {
1246  bytesToWrite = 3;
1247  ch = WUHOO_UNI_REPLACEMENT_CHAR;
1248  }
1249 
1250  target += bytesToWrite;
1251  if (target > targetEnd) {
1252  source = oldSource; /* Back up source pointer! */
1253  target -= bytesToWrite;
1254  result = WuhooTargetExhausted;
1255  break;
1256  }
1257  switch (bytesToWrite) { /* note: everything falls through. */
1258  case 4: *--target = (WuhooUTF8)((ch | byteMark) & byteMask); ch >>= 6;
1259  case 3: *--target = (WuhooUTF8)((ch | byteMark) & byteMask); ch >>= 6;
1260  case 2: *--target = (WuhooUTF8)((ch | byteMark) & byteMask); ch >>= 6;
1261  case 1: *--target = (WuhooUTF8)(ch | WuhooFirstByteMark[bytesToWrite]);
1262  }
1263  target += bytesToWrite;
1264  }
1265  *sourceStart = source;
1266  *targetStart = target;
1267  return result;
1268 }
1269 
1270 WuhooConversionResult
1271 WuhooConvertUTF32toUTF8(const WuhooUTF32** sourceStart,
1272  const WuhooUTF32* sourceEnd, WuhooUTF8** targetStart,
1273  WuhooUTF8* targetEnd, WuhooConversionFlags flags)
1274 {
1275  WuhooConversionResult result = WuhooConversionOK;
1276  const WuhooUTF32* source = *sourceStart;
1277  WuhooUTF8* target = *targetStart;
1278  while (source < sourceEnd) {
1279  WuhooUTF32 ch;
1280  unsigned short bytesToWrite = 0;
1281  const WuhooUTF32 byteMask = 0xBF;
1282  const WuhooUTF32 byteMark = 0x80;
1283  ch = *source++;
1284  if (flags == WuhooStrictConversion) {
1285  /* UTF-16 surrogate values are illegal in UTF-32 */
1286  if (ch >= WUHOO_UNI_SUR_HIGH_START && ch <= WUHOO_UNI_SUR_LOW_END) {
1287  --source; /* return to the illegal value itself */
1288  result = WuhooSourceIllegal;
1289  break;
1290  }
1291  }
1292  /*
1293  * Figure out how many bytes the result will require. Turn any
1294  * illegally large UTF32 things (> Plane 17) into replacement chars.
1295  */
1296  if (ch < (WuhooUTF32)0x80) {
1297  bytesToWrite = 1;
1298  } else if (ch < (WuhooUTF32)0x800) {
1299  bytesToWrite = 2;
1300  } else if (ch < (WuhooUTF32)0x10000) {
1301  bytesToWrite = 3;
1302  } else if (ch <= WUHOO_UNI_MAX_LEGAL_UTF32) {
1303  bytesToWrite = 4;
1304  } else {
1305  bytesToWrite = 3;
1306  ch = WUHOO_UNI_REPLACEMENT_CHAR;
1307  result = WuhooSourceIllegal;
1308  }
1309 
1310  target += bytesToWrite;
1311  if (target > targetEnd) {
1312  --source; /* Back up source pointer! */
1313  target -= bytesToWrite;
1314  result = WuhooTargetExhausted;
1315  break;
1316  }
1317  switch (bytesToWrite) { /* note: everything falls through. */
1318  case 4: *--target = (WuhooUTF8)((ch | byteMark) & byteMask); ch >>= 6;
1319  case 3: *--target = (WuhooUTF8)((ch | byteMark) & byteMask); ch >>= 6;
1320  case 2: *--target = (WuhooUTF8)((ch | byteMark) & byteMask); ch >>= 6;
1321  case 1: *--target = (WuhooUTF8)(ch | WuhooFirstByteMark[bytesToWrite]);
1322  }
1323  target += bytesToWrite;
1324  }
1325  *sourceStart = source;
1326  *targetStart = target;
1327  return result;
1328 }
1329 
1330 #ifdef __APPLE__
1331 
1332 #include <CoreFoundation/CoreFoundation.h>
1333 #include <CoreGraphics/CoreGraphics.h>
1334 #include <Carbon/Carbon.h> /* Mainly for keycodes */
1335 #//define OBJC_OLD_DISPATCH_PROTOTYPES 1
1336 #include <objc/objc.h>
1337 #include <objc/objc-runtime.h>
1338 #include <objc/NSObjCRuntime.h>
1339 
1340 //#include <OpenGL/CGLTypes.h>
1341 #include <OpenGL/OpenGL.h>
1342 #include <OpenGL/gl.h>
1343 
1344 #include <ApplicationServices/ApplicationServices.h>
1345 
1346 #ifdef __cplusplus
1347 #define OBJC_OBJECT_CAST(object) ();
1348 #else
1349 #endif
1350 
1351 #define WuhooMaybeUnused __attribute__((unused))
1352 
1353 extern CFStringRef NSPasteboardNameDrag;
1354 
1355 #ifdef __cplusplus
1356 extern "C"
1357 {
1358 #endif
1359 
1360  SEL
1361  NSSelectorFromString(CFStringRef str);
1362  int
1363  NSRunAlertPanel(CFStringRef strTitle, CFStringRef strMsg,
1364  CFStringRef strButton1, CFStringRef strButton2,
1365  CFStringRef strButton3, ...);
1366 
1367 typedef id(*WuhooObjMsgSendCb)(id, SEL, ...);
1368 typedef id(*WuhooObjMsgSendStretCb)(id, SEL, ...);
1369 
1370 typedef id(*CMacsSimpleMessage)(id, SEL);
1371 typedef void(*CMacsVoidMessage)(id, SEL);
1372 typedef void(*CMacsVoidMessage1)(id, SEL, void *);
1373 typedef id(*CMacsRectMessage1)(id, SEL, CGRect);
1374 typedef id(*CMacsWindowInitMessage)(id, SEL, CGRect, int, int, bool);
1375 
1376 WuhooObjMsgSendCb WuhooObjMsgSend = (WuhooObjMsgSendCb)objc_msgSend;
1377 WuhooObjMsgSendStretCb WuhooObjMsgSendStret = (WuhooObjMsgSendStretCb)objc_msgSend_stret;
1378 
1379 #ifdef __cplusplus
1380 }
1381 #endif
1382 
1383 typedef struct
1384 {
1385  id app;
1386  id window;
1387  id view;
1388  CGImageRef image;
1389  id bitmap;
1390  id cgctx;
1391  id pool;
1392  id glctx;
1393  id pixelformat;
1394  id future_time;
1395 
1396  Class WuhooNSView;
1397  Class WuhooNSWindow;
1398 
1399  SEL get_type;
1400  SEL get_subtype;
1401  SEL get_clickcount;
1402  SEL get_characters;
1403  SEL get_modifier_flags;
1404  SEL get_keycode;
1405 
1406  SEL get_distant_future;
1407  SEL get_scrolling_delta_x;
1408  SEL get_scrolling_delta_y;
1409  SEL get_delta_x;
1410  SEL get_delta_y;
1411 
1412  WuhooRGBA* buffer;
1413 
1414  NSUInteger window_mask;
1415 
1416  unsigned int prev_dead_char;
1417  int previous_change_count;
1418  int client_offset;
1419  int title_pixel_offset;
1420 } WuhooWindowCocoa;
1421 
1422 #define TO_ID(id) (struct objc_object*)(id)
1423 
1424 enum
1425  {
1426  /* @see NSWindowStyleMask */
1427  WuhooNSWindowStyleMaskBorderless = 0,
1428  WuhooNSWindowStyleMaskTitled = 1 << 0,
1429  WuhooNSWindowStyleMaskClosable = 1 << 1,
1430  WuhooNSWindowStyleMaskMiniaturizable = 1 << 2,
1431  WuhooNSWindowStyleMaskResizable = 1 << 3,
1432  };
1433 
1434 static const char* notification_name_cache[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
1435 
1436 static int luda = 0;
1437 
1438 void
1439 _WuhooNotificationCenterCallback(CFNotificationCenterRef center, void* observer,
1440  CFStringRef name, const void* object,
1441  CFDictionaryRef userInfo)
1442 {
1443  const char* raw_name = CFStringGetCStringPtr(name, kCFStringEncodingUTF8);
1444  WuhooWindow* window = (WuhooWindow*)observer;
1445  WuhooWindowCocoa* cocoa_window;
1446  id window_responder = (struct objc_object*)object;
1447 
1448  if (notification_name_cache[0] == raw_name ||
1449  0 == strcmp("NSWindowDidCloseNotification", raw_name)) {
1450  window->is_alive = WuhooNo;
1451 
1452  } else if (notification_name_cache[1] == raw_name ||
1453  0 == strcmp("NSWindowDidResizeNotification", raw_name)) {
1454  if (WuhooNull == window_responder)
1455  return;
1456 
1457  cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1458 
1459  id view_responder =
1460  WuhooObjMsgSend(window_responder, sel_registerName("contentView"));
1461  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
1462  view_responder, sel_registerName("frame"));
1463  window->cwidth = rect.size.width;
1464  window->cheight = rect.size.height;
1465  rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(cocoa_window->window,
1466  sel_getUid("frame"));
1467  window->width = rect.size.width;
1468  window->height = rect.size.height;
1469 
1470  window->window_flags |= WUHOO_WINDOW_FLAG_RESIZED;
1471  } else if (notification_name_cache[2] == raw_name ||
1472  0 == strcmp("NSWindowDidEnterFullScreenNotification", raw_name)) {
1473  if (WuhooNull == window_responder)
1474  return;
1475 
1476  cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1477 
1478  id view_responder =
1479  WuhooObjMsgSend(window_responder, sel_registerName("contentView"));
1480  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
1481  view_responder, sel_registerName("frame"));
1482  window->cwidth = rect.size.width;
1483  window->cheight = rect.size.height;
1484  rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(cocoa_window->window,
1485  sel_getUid("frame"));
1486  window->width = rect.size.width;
1487  window->height = rect.size.height;
1488 
1489  /* This seems correct for now */
1490  window->window_flags |=
1491  WUHOO_WINDOW_FLAG_FULL_SCREEN | WUHOO_WSTATE_MAXIMIZED;
1492  } else if (notification_name_cache[3] == raw_name ||
1493  0 == strcmp("NSWindowDidBecomeKeyNotification", raw_name)) {
1494  window->window_flags |= WUHOO_WINDOW_FLAG_FOCUS_GAINED;
1495  } else if (notification_name_cache[4] == raw_name ||
1496  0 == strcmp("NSWindowDidResignKeyNotification", raw_name)) {
1497  window->window_flags |= WUHOO_WINDOW_FLAG_FOCUS_LOST;
1498  } else if (notification_name_cache[5] == raw_name ||
1499  0 == strcmp("NSWindowDidMiniaturizeNotification", raw_name)) {
1500  window->window_flags |= WUHOO_WINDOW_FLAG_MINIMIZED;
1501  } else if (notification_name_cache[6] == raw_name ||
1502  0 == strcmp("NSWindowDidMoveNotification", raw_name)) {
1503  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
1504  window_responder, sel_registerName("frame"));
1505  window->x = rect.origin.x;
1506  window->y = rect.origin.y;
1507  window->window_flags |= WUHOO_WINDOW_FLAG_MOVED;
1508  } else if (notification_name_cache[7] == raw_name ||
1509  0 == strcmp("NSWindowDidEndLiveResizeNotification", raw_name)) {
1510  if (WuhooNull == window_responder)
1511  return;
1512 
1513  cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1514 
1515  id view_responder =
1516  WuhooObjMsgSend(window_responder, sel_registerName("contentView"));
1517  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
1518  view_responder, sel_registerName("frame"));
1519  window->cwidth = rect.size.width;
1520  window->cheight = rect.size.height;
1521  rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(cocoa_window->window,
1522  sel_getUid("frame"));
1523  window->width = rect.size.width;
1524  window->height = rect.size.height;
1525 
1526  window->window_flags |= WUHOO_WINDOW_FLAG_RESIZED;
1527  } else {
1528  }
1529 }
1530 
1531 WuhooInternal WuhooKeyModifiers
1532 _WuhooKeyModifiersExtractCocoa(NSUInteger mod_flags)
1533 {
1534  int mods = WUHOO_KMOD_NONE;
1535 
1536  mods |= (NX_DEVICELSHIFTKEYMASK & mod_flags) ? WUHOO_KMOD_LSHIFT : 0;
1537  mods |= (NX_DEVICERSHIFTKEYMASK & mod_flags) ? WUHOO_KMOD_RSHIFT : 0;
1538  mods |= (NX_DEVICELCTLKEYMASK & mod_flags) ? WUHOO_KMOD_LCTRL : 0;
1539  mods |= (NX_DEVICERCTLKEYMASK & mod_flags) ? WUHOO_KMOD_RCTRL : 0;
1540  mods |= (NX_DEVICELALTKEYMASK & mod_flags) ? WUHOO_KMOD_LALT : 0;
1541  mods |= (NX_DEVICERALTKEYMASK & mod_flags) ? WUHOO_KMOD_RALT : 0;
1542  mods |= (NX_DEVICELCMDKEYMASK & mod_flags) ? WUHOO_KMOD_LGUI : 0;
1543  mods |= (NX_DEVICERCMDKEYMASK & mod_flags) ? WUHOO_KMOD_RGUI : 0;
1544  mods |= (NX_ALPHASHIFTMASK & mod_flags) ? WUHOO_KMOD_CAPS : 0;
1545  mods |= (NX_SECONDARYFNMASK & mod_flags) ? WUHOO_KMOD_MODE : 0;
1546 
1547  return (WuhooKeyModifiers)mods;
1548 }
1549 
1550 WuhooInternal WuhooKeyCode
1551 _WuhooKeyTranslateCocoa(unsigned int key_code)
1552 {
1553  switch (key_code) {
1554  /* Undocumented Kye COde for Mac OS X */
1555  case 0x6e: return WUHOO_VKEY_MENU; break;
1556  case kVK_ANSI_Comma: return WUHOO_VKEY_COMMA; break;
1557  case kVK_ANSI_Semicolon: return WUHOO_VKEY_SEMICOLON; break;
1558  case kVK_ANSI_Backslash: return WUHOO_VKEY_BACK_SLASH; break;
1559  case kVK_ANSI_Slash: return WUHOO_VKEY_FORWARD_SLASH; break;
1560  case kVK_ANSI_Quote: return WUHOO_VKEY_QUOTE; break;
1561  case kVK_ANSI_LeftBracket: return WUHOO_VKEY_LEFT_BRACKET; break;
1562  case kVK_ANSI_RightBracket: return WUHOO_VKEY_RIGHT_BRACKET; break;
1563  case kVK_ANSI_Equal: return WUHOO_VKEY_EQUALS; break;
1564  case kVK_ANSI_Minus: return WUHOO_VKEY_MINUS; break;
1565  case kVK_ANSI_Period: return WUHOO_VKEY_PERIOD; break;
1566  case kVK_ANSI_Grave: return WUHOO_VKEY_GRAVE; break;
1567  case 0xa: return WUHOO_VKEY_GRAVE; break;
1568  case kVK_ANSI_KeypadDecimal: return WUHOO_VKEY_KPAD_DECIMAL; break;
1569  case kVK_ANSI_KeypadMultiply: return WUHOO_VKEY_KPAD_MULITPLY; break;
1570  case kVK_ANSI_KeypadPlus: return WUHOO_VKEY_KPAD_PLUS; break;
1571  case kVK_ANSI_KeypadClear: return WUHOO_VKEY_KPAD_NUM_LOCK; break;
1572  case kVK_ANSI_KeypadDivide: return WUHOO_VKEY_KPAD_DIVIDE; break;
1573  case kVK_ANSI_KeypadEnter: return WUHOO_VKEY_KPAD_ENTER; break;
1574  case kVK_ANSI_KeypadMinus: return WUHOO_VKEY_KPAD_MINUS; break;
1575  case kVK_ANSI_KeypadEquals: return WUHOO_VKEY_KPAD_EQUALS; break;
1576  case kVK_ANSI_Keypad0: return WUHOO_VKEY_KPAD_0; break;
1577  case kVK_ANSI_Keypad1: return WUHOO_VKEY_KPAD_1; break;
1578  case kVK_ANSI_Keypad2: return WUHOO_VKEY_KPAD_2; break;
1579  case kVK_ANSI_Keypad3: return WUHOO_VKEY_KPAD_3; break;
1580  case kVK_ANSI_Keypad4: return WUHOO_VKEY_KPAD_4; break;
1581  case kVK_ANSI_Keypad5: return WUHOO_VKEY_KPAD_5; break;
1582  case kVK_ANSI_Keypad6: return WUHOO_VKEY_KPAD_6; break;
1583  case kVK_ANSI_Keypad7: return WUHOO_VKEY_KPAD_7; break;
1584  case kVK_ANSI_Keypad8: return WUHOO_VKEY_KPAD_8; break;
1585  case kVK_ANSI_Keypad9: return WUHOO_VKEY_KPAD_9; break;
1586  case kVK_ANSI_0: return WUHOO_VKEY_0; break;
1587  case kVK_ANSI_1: return WUHOO_VKEY_1; break;
1588  case kVK_ANSI_2: return WUHOO_VKEY_2; break;
1589  case kVK_ANSI_3: return WUHOO_VKEY_3; break;
1590  case kVK_ANSI_4: return WUHOO_VKEY_4; break;
1591  case kVK_ANSI_5: return WUHOO_VKEY_5; break;
1592  case kVK_ANSI_6: return WUHOO_VKEY_6; break;
1593  case kVK_ANSI_7: return WUHOO_VKEY_7; break;
1594  case kVK_ANSI_8: return WUHOO_VKEY_8; break;
1595  case kVK_ANSI_9: return WUHOO_VKEY_9; break;
1596  case kVK_ANSI_A: return WUHOO_VKEY_A; break;
1597  case kVK_ANSI_B: return WUHOO_VKEY_B; break;
1598  case kVK_ANSI_C: return WUHOO_VKEY_C; break;
1599  case kVK_ANSI_D: return WUHOO_VKEY_D; break;
1600  case kVK_ANSI_E: return WUHOO_VKEY_E; break;
1601  case kVK_ANSI_F: return WUHOO_VKEY_F; break;
1602  case kVK_ANSI_G: return WUHOO_VKEY_G; break;
1603  case kVK_ANSI_H: return WUHOO_VKEY_H; break;
1604  case kVK_ANSI_I: return WUHOO_VKEY_I; break;
1605  case kVK_ANSI_J: return WUHOO_VKEY_J; break;
1606  case kVK_ANSI_K: return WUHOO_VKEY_K; break;
1607  case kVK_ANSI_L: return WUHOO_VKEY_L; break;
1608  case kVK_ANSI_M: return WUHOO_VKEY_M; break;
1609  case kVK_ANSI_N: return WUHOO_VKEY_N; break;
1610  case kVK_ANSI_O: return WUHOO_VKEY_O; break;
1611  case kVK_ANSI_P: return WUHOO_VKEY_P; break;
1612  case kVK_ANSI_Q: return WUHOO_VKEY_Q; break;
1613  case kVK_ANSI_R: return WUHOO_VKEY_R; break;
1614  case kVK_ANSI_S: return WUHOO_VKEY_S; break;
1615  case kVK_ANSI_T: return WUHOO_VKEY_T; break;
1616  case kVK_ANSI_U: return WUHOO_VKEY_U; break;
1617  case kVK_ANSI_V: return WUHOO_VKEY_V; break;
1618  case kVK_ANSI_W: return WUHOO_VKEY_W; break;
1619  case kVK_ANSI_X: return WUHOO_VKEY_X; break;
1620  case kVK_ANSI_Y: return WUHOO_VKEY_Y; break;
1621  case kVK_ANSI_Z: return WUHOO_VKEY_Z; break;
1622  case kVK_LeftArrow: return WUHOO_VKEY_LEFT; break;
1623  case kVK_RightArrow: return WUHOO_VKEY_RIGHT; break;
1624  case kVK_UpArrow: return WUHOO_VKEY_UP; break;
1625  case kVK_DownArrow: return WUHOO_VKEY_DOWN; break;
1626  case kVK_Escape: return WUHOO_VKEY_ESCAPE; break;
1627  case kVK_Return: return WUHOO_VKEY_ENTER; break;
1628  case kVK_Tab: return WUHOO_VKEY_TAB; break;
1629  case kVK_Space: return WUHOO_VKEY_SPACE; break;
1630  case kVK_Delete: return WUHOO_VKEY_DELETE; break;
1631  case kVK_Command: break;
1632  case kVK_Shift: break;
1633  case kVK_CapsLock: break;
1634  case kVK_Option: break;
1635  case kVK_Control: break;
1636  case kVK_RightCommand: break;
1637  case kVK_RightShift: break;
1638  case kVK_RightOption: break;
1639  case kVK_RightControl: break;
1640  case kVK_Function: break;
1641  case kVK_F17: break;
1642  case kVK_VolumeUp: break;
1643  case kVK_VolumeDown: break;
1644  case kVK_Mute: break;
1645  case kVK_F18: break;
1646  case kVK_F19: break;
1647  case kVK_F20: break;
1648  case kVK_F1: return WUHOO_VKEY_F1; break;
1649  case kVK_F2: return WUHOO_VKEY_F2; break;
1650  case kVK_F3: return WUHOO_VKEY_F3; break;
1651  case kVK_F4: return WUHOO_VKEY_F4; break;
1652  case kVK_F5: return WUHOO_VKEY_F5; break;
1653  case kVK_F6: return WUHOO_VKEY_F6; break;
1654  case kVK_F7: return WUHOO_VKEY_F7; break;
1655  case kVK_F8: return WUHOO_VKEY_F8; break;
1656  case kVK_F9: return WUHOO_VKEY_F9; break;
1657  case kVK_F10: return WUHOO_VKEY_F10; break;
1658  case kVK_F11: return WUHOO_VKEY_F11; break;
1659  case kVK_F12: return WUHOO_VKEY_F12; break;
1660  case kVK_F13: return WUHOO_VKEY_F13; break;
1661  case kVK_F16: break;
1662  case kVK_F14: break;
1663  case kVK_F15: break;
1664  case kVK_Help: return WUHOO_VKEY_INSERT; break;
1665  case kVK_Home: return WUHOO_VKEY_HOME; break;
1666  case kVK_PageUp: return WUHOO_VKEY_PAGE_UP; break;
1667  case kVK_ForwardDelete: return WUHOO_VKEY_BACKSPACE; break;
1668  case kVK_End: return WUHOO_VKEY_END; break;
1669  case kVK_PageDown: return WUHOO_VKEY_PAGE_DOWN; break;
1670  default: return WUHOO_VKEY_UNKNOWN; break;
1671  }
1672 
1673  return WUHOO_VKEY_UNKNOWN;
1674 }
1675 
1676 WuhooResult
1677 _WuhooWindowDropContentsGetCocoa(WuhooWindow* window, WuhooEvent* event,
1678  char* buffer, WuhooSize buffer_size)
1679 {
1680  WuhooZeroInit(buffer, buffer_size);
1681 
1682  id pis = (id)event->data.drop.context;
1683 
1684  char* memory_offset = buffer;
1685  for (NSUInteger item_index = 0; item_index < event->data.drop.count;
1686  item_index++) {
1687  id pi = WuhooObjMsgSend(pis, sel_getUid("objectAtIndex:"), item_index);
1688 
1689  id pi_data = WuhooObjMsgSend(pi, sel_getUid("dataForType:"), kUTTypeFileURL);
1690  id urlString =
1691  WuhooObjMsgSend(TO_ID(objc_getClass("NSString")), sel_getUid("alloc"));
1692  urlString =
1693  WuhooObjMsgSend(urlString, sel_getUid("initWithData:encoding:"), pi_data, 4);
1694 
1695  const char* utf8_chars =
1696  (const char*)WuhooObjMsgSend(urlString, sel_getUid("UTF8String"));
1697 
1698  id url = WuhooObjMsgSend(TO_ID(objc_getClass("NSURL")), sel_getUid("alloc"));
1699  url = WuhooObjMsgSend(url, sel_getUid("initWithString:"), urlString);
1700  id url_path = WuhooObjMsgSend(url, sel_getUid("path"));
1701  utf8_chars = (const char*)WuhooObjMsgSend(url_path, sel_getUid("UTF8String"));
1702  size_t bytes_copied =
1703  WuhooStringCopy(memory_offset, utf8_chars, WUHOO_MAX_FILE_NAME_LENGTH);
1704  memory_offset += bytes_copied;
1705  *memory_offset++ = '\n';
1706  }
1707 
1708  WuhooObjMsgSend(pis, sel_getUid("release"));
1709 
1710  return WuhooSuccess;
1711 }
1712 
1713 WuhooBoolean
1714 _WuhooDropHandleCocoa(WuhooWindow* window, WuhooEvent* event)
1715 {
1716  WuhooWindowCocoa* cocoa_window;
1717  cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1718 
1719  id pb = WuhooObjMsgSend(TO_ID(objc_getClass("NSPasteboard")),
1720  sel_getUid("pasteboardWithName:"), NSPasteboardNameDrag);
1721  NSInteger changeCount =
1722  (NSInteger)WuhooObjMsgSend(pb, sel_getUid("changeCount"));
1723  NSUInteger mouse_pressed = (NSUInteger)WuhooObjMsgSend(
1724  TO_ID(objc_getClass("NSEvent")), sel_getUid("pressedMouseButtons"));
1725 
1726  if (window->window_flags & WUHOO_WINDOW_FLAG_DROP_STARTED) {
1727  if (mouse_pressed == 0) {
1728  window->window_flags &= ~WUHOO_WINDOW_FLAG_DROP_STARTED;
1729 
1730  CGPoint mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
1731  (struct objc_object*)objc_getClass("NSEvent"),
1732  sel_registerName("mouseLocation"));
1733  NSInteger window_id = (NSInteger)WuhooObjMsgSend(
1734  (struct objc_object*)objc_getClass("NSWindow"),
1735  sel_getUid("windowNumberAtPoint:belowWindowWithWindowNumber:"), mouse,
1736  0);
1737  NSInteger window_id_ours = (NSInteger)WuhooObjMsgSend(
1738  cocoa_window->window, sel_getUid("windowNumber"));
1739 
1740  if (window_id_ours != window_id) {
1741  return WuhooNo;
1742  }
1743 
1744  id pis = WuhooObjMsgSend(pb, sel_getUid("pasteboardItems"));
1745  /* We specifacally handle the memory for this */
1746  WuhooObjMsgSend(pis, sel_getUid("retain"));
1747  NSUInteger pis_count = (NSUInteger)WuhooObjMsgSend(pis, sel_getUid("count"));
1748 
1749  int bytes_required = 0;
1750  for (NSUInteger item_index = 0; item_index < pis_count; item_index++) {
1751  id pi = WuhooObjMsgSend(pis, sel_getUid("objectAtIndex:"), item_index);
1752 
1753  id pi_data =
1754  WuhooObjMsgSend(pi, sel_getUid("dataForType:"), kUTTypeFileURL);
1755  id urlString =
1756  WuhooObjMsgSend(TO_ID(objc_getClass("NSString")), sel_getUid("alloc"));
1757  urlString = WuhooObjMsgSend(
1758  urlString, sel_getUid("initWithData:encoding:"), pi_data, 4);
1759 
1760  const char* utf8_chars =
1761  (const char*)WuhooObjMsgSend(urlString, sel_getUid("UTF8String"));
1762 
1763  id url =
1764  WuhooObjMsgSend(TO_ID(objc_getClass("NSURL")), sel_getUid("alloc"));
1765  url = WuhooObjMsgSend(url, sel_getUid("initWithString:"), urlString);
1766  id url_path = WuhooObjMsgSend(url, sel_getUid("path"));
1767  utf8_chars =
1768  (const char*)WuhooObjMsgSend(url_path, sel_getUid("UTF8String"));
1769  bytes_required +=
1770  WuhooStringLength(utf8_chars, WUHOO_MAX_FILE_NAME_LENGTH) + 1;
1771  }
1772 
1773  event->data.drop.context = (WuhooHandle)pis;
1774  event->data.drop.size = bytes_required + 1;
1775  event->data.drop.count = (WuhooSize)pis_count;
1776  event->type = WUHOO_EVT_DROP;
1777 
1778  return WuhooYes;
1779  } else {
1780  cocoa_window->previous_change_count = (int)changeCount;
1781  return WuhooNo;
1782  }
1783  }
1784 
1785  if (cocoa_window->previous_change_count == changeCount ||
1786  cocoa_window->previous_change_count < 0) {
1787  cocoa_window->previous_change_count = (int)changeCount;
1788  return WuhooNo;
1789  }
1790 
1791  if (mouse_pressed == 1) {
1792  cocoa_window->previous_change_count = (int)changeCount;
1793  window->window_flags |= WUHOO_WINDOW_FLAG_DROP_STARTED;
1794  }
1795 
1796  return WuhooNo;
1797 }
1798 
1799 WuhooResult
1800 _WuhooWindowSetTitleCocoa(WuhooWindow* window, const char* title)
1801 {
1802  WuhooWindowCocoa* cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1803  WuhooResult result = WuhooSuccess;
1804 
1805  CFStringRef window_title =
1806  CFStringCreateWithCString(WuhooNull, title, kCFStringEncodingUTF8);
1807  WuhooObjMsgSend(cocoa_window->window, sel_registerName("setTitle:"),
1808  window_title);
1809  CFRelease(window_title);
1810 
1811  return result;
1812 }
1813 
1814 WuhooResult
1815 _WuhooWindowRegionGetCocoa(WuhooWindow* window, int* posx, int* posy,
1816  WuhooSize* width, WuhooSize* height)
1817 {
1818  WuhooResult result = WuhooSuccess;
1819 
1820  WuhooWindowCocoa* cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1821 
1822  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(cocoa_window->window,
1823  sel_getUid("frame"));
1824 
1825  if (WuhooNull != posx)
1826  *posx = rect.origin.x;
1827  if (WuhooNull != posy)
1828  *posy = rect.origin.y;
1829  if (WuhooNull != width)
1830  *width = rect.size.width;
1831  if (WuhooNull != height)
1832  *height = rect.size.height;
1833 
1834  return result;
1835 }
1836 
1837 WuhooResult
1838 _WuhooWindowClientRegionGetCocoa(WuhooWindow* window, int* posx, int* posy,
1839  WuhooSize* width, WuhooSize* height)
1840 {
1841  WuhooResult result = WuhooSuccess;
1842 
1843  WuhooWindowCocoa* cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1844 
1845  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(cocoa_window->view,
1846  sel_getUid("frame"));
1847 
1848  if (WuhooNull != posx)
1849  *posx = rect.origin.x;
1850  if (WuhooNull != posy)
1851  *posy = rect.origin.y;
1852  if (WuhooNull != width)
1853  *width = rect.size.width;
1854  if (WuhooNull != height)
1855  *height = rect.size.height;
1856 
1857  return result;
1858 }
1859 
1860 WuhooResult
1861 _WuhooWindowClientRegionSetCocoa(WuhooWindow* window, int posx, int posy,
1862  WuhooSize width, WuhooSize height)
1863 {
1864  WuhooResult result = WuhooSuccess;
1865 
1866  WuhooWindowCocoa* cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1867 
1868  CGRect test;
1869  test.origin.x = posx;
1870  test.origin.y = posy;
1871  test.size.width = width;
1872  test.size.height = height;
1873 
1874  CGRect client_rect =
1875  ((CGRect(*)(id, SEL, CGRect, unsigned int))WuhooObjMsgSendStret)(
1876  (struct objc_object*)objc_getClass("NSWindow"),
1877  sel_registerName("contentRectForFrameRect:styleMask:"), test,
1878  (unsigned int)cocoa_window->window_mask);
1879  int title_height_diff = (int)height - client_rect.size.height;
1880 
1881  CGRect rect;
1882  rect.origin.x = posx;
1883  rect.origin.y = posy;
1884  rect.size.width = width;
1885  rect.size.height = height + title_height_diff;
1886 
1887  window->width = rect.size.width;
1888  window->height = rect.size.height;
1889  window->x = rect.origin.x;
1890  window->y = rect.origin.y;
1891 
1892  WuhooObjMsgSend(cocoa_window->window, sel_registerName("setFrame:display:"),
1893  rect, YES);
1894 
1895  return result;
1896 }
1897 
1898 WuhooResult
1899 _WuhooWindowRegionSetCocoa(WuhooWindow* window, int posx, int posy, int width,
1900  int height)
1901 {
1902  WuhooResult result = WuhooSuccess;
1903 
1904  WuhooWindowCocoa* cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1905 
1906  CGRect test;
1907  test.origin.x = posx;
1908  test.origin.y = posy;
1909  test.size.width = width;
1910  test.size.height = height;
1911 
1912  CGRect rect;
1913  rect.origin.x = posx;
1914  rect.origin.y = posy;
1915  rect.size.width = width;
1916  rect.size.height = height;
1917 
1918  window->width = rect.size.width;
1919  window->height = rect.size.height;
1920  window->x = rect.origin.x;
1921  window->y = rect.origin.y;
1922 
1923  WuhooObjMsgSend(cocoa_window->window, sel_registerName("setFrame:display:"),
1924  rect, YES);
1925 
1926  return result;
1927 }
1928 
1929 WuhooResult
1930 _WuhooWindowDestroyCocoa(WuhooWindow* window)
1931 {
1932  WuhooResult result = WuhooSuccess;
1933  WuhooWindowCocoa* cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1934 
1935 #ifdef WUHOO_OPENGL_ENABLE
1936  if (WUHOO_FLAG_OPENGL & window->flags) {
1937  WuhooObjMsgSend((struct objc_object*)objc_getClass("NSGraphicsContext"),
1938  sel_getUid("setCurrentContext:"), WuhooNull);
1939  }
1940 #endif
1941 
1942  WuhooObjMsgSend(cocoa_window->pool, sel_registerName("release"));
1943 
1944  return result;
1945 }
1946 
1947 CGRect Window_constrainFrameRect(id self, SEL _cmd, CGRect rect, void* screen) {
1948  return rect;
1949 }
1950 
1951 void View_drawRect(id self, SEL _cmd, CGRect rect) {
1952  //Ivar ivar = class_getInstanceVariable(cocoa_window->WuhooNSView, "_wheels");
1953  Ivar ivar = class_getInstanceVariable(objc_getClass("WuhooNSView"), "_wheels");
1954 
1955  id tom = object_getIvar(self, ivar);
1956  WuhooWindow* window = (WuhooWindow*)tom;
1957  WuhooWindowCocoa* cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1958 
1959  if (WuhooNull == cocoa_window->image)
1960  return;
1961 
1962  id cgctx = WuhooObjMsgSend((id)objc_getClass("NSGraphicsContext"), sel_getUid("currentContext"));
1963 
1964  CGContextRef cgctxref =
1965  (CGContextRef)WuhooObjMsgSend(cgctx, sel_getUid("CGContext"));
1966 
1967  int width = window->cwidth;
1968  int height = window->cheight;
1969 
1970  CGContextDrawImage(cgctxref, CGRectMake(0, 0, width, height),
1971  cocoa_window->image);
1972 
1973  CGImageRelease(cocoa_window->image);
1974 
1975  cocoa_window->image = WuhooNull;
1976 }
1977 
1978 WuhooResult
1979 _WuhooWindowBlitCocoa(WuhooWindow* window, WuhooRGBA* pixels, WuhooSize x,
1980  WuhooSize y, WuhooSize width, WuhooSize height)
1981 {
1982  WuhooResult result = WuhooSuccess;
1983  WuhooWindowCocoa* cocoa_window = (WuhooWindowCocoa*)window->platform_window;
1984 
1985  id pool =
1986  WuhooObjMsgSend((struct objc_object*)objc_getClass("NSAutoreleasePool"),
1987  sel_registerName("alloc"));
1988  pool = WuhooObjMsgSend(pool, sel_registerName("init"));
1989 
1990  //WuhooObjMsgSend(cocoa_window->view, sel_getUid("lockFocus"));
1991 
1992 #ifdef WUHOO_OPENGL_ENABLE
1993  if (WUHOO_FLAG_OPENGL & window->flags) {
1994 
1995  WuhooObjMsgSend(cocoa_window->glctx, sel_getUid("update"));
1996  WuhooObjMsgSend(cocoa_window->glctx, sel_getUid("flushBuffer"));
1997 
1998  WuhooObjMsgSend(pool, sel_getUid("release"));
1999 
2000  return WuhooSuccess;
2001  }
2002 #endif
2003 
2004  if (WUHOO_WINDOW_FLAG_CLOSED & window->window_flags ||
2005  WUHOO_WINDOW_FLAG_RESIZED & window->window_flags ||
2006  WUHOO_WINDOW_FLAG_REGION_UPDATED & window->window_flags) {
2007  /* If important window states have not been handled yet
2008  * don not proceed with blitting because this might
2009  * cause inconcistent behaviors. **REVISIT**
2010  */
2011  return WuhooSuccess;
2012  }
2013 
2014  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
2015  CGDataProviderRef provider = CGDataProviderCreateWithData(
2016  NULL, pixels, width * height * sizeof(*pixels), NULL);
2017 
2018  cocoa_window->image =
2019  CGImageCreate(width, height, 8, 32, width * sizeof(*pixels), colorSpace,
2020  kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast, provider,
2021  NULL, true, kCGRenderingIntentDefault);
2022 
2023  CGRect invalidated_rect = CGRectMake(0, 0, width, height);
2024 
2025  WuhooObjMsgSend(cocoa_window->view,
2026  sel_registerName("setNeedsDisplayInRect:"), invalidated_rect);
2027 
2028  CGColorSpaceRelease(colorSpace);
2029 
2030  //WuhooObjMsgSend(cocoa_window->view, sel_getUid("unlockFocus"));
2031  WuhooObjMsgSend(pool, sel_getUid("release"));
2032 
2033  return result;
2034 }
2035 
2036 //-------------------------------------------------------------------------
2037 // translate keyDown to true unichar via inputlayout
2038 //-------------------------------------------------------------------------
2039 WuhooResult
2040 WuhooWindowChracterDecodeCocoa(WuhooWindow* window, WuhooEvent* event,
2041  id cocoa_event)
2042 {
2043  WuhooWindowCocoa* cocoa_window;
2044  cocoa_window = (WuhooWindowCocoa*)window->platform_window;
2045  // http://stackoverflow.com/questions/12547007/convert-key-code-into-key-equivalent-string
2046  // http://stackoverflow.com/questions/8263618/convert-virtual-key-code-to-unicode-string
2047 
2048  id chars = WuhooObjMsgSend(cocoa_event, cocoa_window->get_characters);
2049  const char* utf8_chars =
2050  (const char*)WuhooObjMsgSend(chars, sel_getUid("UTF8String"));
2051  const size_t unicodeStringLength = 4;
2052  UniChar unicodeString[unicodeStringLength] = { 0 };
2053  UniCharCount reallength = 0;
2054 
2055  TISInputSourceRef fkis = TISCopyCurrentKeyboardInputSource();
2056  unsigned short key_code = (unsigned short)((NSUInteger)WuhooObjMsgSend(
2057  cocoa_event, cocoa_window->get_keycode));
2058  NSUInteger mod_flags =
2059  (NSUInteger)WuhooObjMsgSend(cocoa_event, cocoa_window->get_modifier_flags);
2060 
2061  if (fkis) {
2062  CFDataRef cflayoutdata = (CFDataRef)TISGetInputSourceProperty(
2063  fkis, kTISPropertyUnicodeKeyLayoutData);
2064  const UCKeyboardLayout* keyboardlayout =
2065  (const UCKeyboardLayout*)CFDataGetBytePtr(cflayoutdata);
2066  CGEventFlags flags = mod_flags;
2067  UInt32 keymodifiers = (flags >> 16) & 0xFF;
2068 
2069  UCKeyTranslate(keyboardlayout, key_code, kUCKeyActionDown, keymodifiers,
2070  LMGetKbdType(), 0, &cocoa_window->prev_dead_char,
2071  unicodeStringLength, &reallength, unicodeString);
2072  CFRelease(fkis);
2073  }
2074 
2075  event->type = WUHOO_EVT_KEY;
2076  event->data.key.state = WUHOO_KSTATE_DOWN;
2077  event->data.key.code = _WuhooKeyTranslateCocoa(key_code);
2078  event->data.key.mods = _WuhooKeyModifiersExtractCocoa(mod_flags);
2079  event->data.key.mods |=
2080  (1 == cocoa_window->prev_dead_char) ? (int)WUHOO_KMOD_DEADCHAR : 0;
2081  if (reallength > 0) {
2082  id nsstring = (id)CFStringCreateWithCharacters(kCFAllocatorDefault,
2083  unicodeString, reallength);
2084  utf8_chars = (const char*)WuhooObjMsgSend(nsstring, sel_getUid("UTF8String"));
2085  WuhooCharacterCopy((char *)event->data.key.character, utf8_chars);
2086  CFRelease(nsstring);
2087  } else {
2088  WuhooCharacterCopy((char*)event->data.key.character, utf8_chars);
2089  }
2090 
2091  return WuhooNull;
2092 }
2093 
2094 WuhooResult
2095 _WuhooWindowEventNextCocoa(WuhooWindow* window, WuhooEvent* event)
2096 {
2097  WuhooWindowCocoa* cocoa_window;
2098  unsigned short key_code;
2099  NSUInteger mod_flags;
2100 
2101  if (WuhooNo == window->is_alive) {
2102  event->type = WUHOO_EVT_WINDOW;
2103  event->data.window.state = WUHOO_WSTATE_CLOSED;
2104 
2105  return WuhooSuccess;
2106  }
2107 
2108  cocoa_window = (WuhooWindowCocoa*)window->platform_window;
2109 
2110  if (window->window_flags & WUHOO_WINDOW_FLAG_RESIZED) {
2111  event->type = WUHOO_EVT_WINDOW;
2112  event->data.window.state = WUHOO_WSTATE_RESIZED;
2113  event->data.window.data1 = window->cwidth;
2114  event->data.window.data2 = window->cheight;
2115  event->data.window.flags = WUHOO_WINDOW_FLAG_RESIZED;
2116  window->window_flags &= ~WUHOO_WINDOW_FLAG_RESIZED;
2117 
2118  return WuhooSuccess;
2119  } else if (window->window_flags & WUHOO_WINDOW_FLAG_FULL_SCREEN) {
2120  event->type = WUHOO_EVT_WINDOW;
2121  event->data.window.state = WUHOO_WSTATE_RESIZED;
2122  event->data.window.data1 = window->cwidth;
2123  event->data.window.data2 = window->cheight;
2124  event->data.window.flags = (WuhooWindowFlags) (WUHOO_WINDOW_FLAG_RESIZED | WUHOO_WINDOW_FLAG_FULL_SCREEN);
2125  window->window_flags &= ~WUHOO_WINDOW_FLAG_FULL_SCREEN;
2126 
2127  return WuhooSuccess;
2128  } else if (window->window_flags & WUHOO_WINDOW_FLAG_REGION_UPDATED) {
2129  event->type = WUHOO_EVT_WINDOW;
2130  event->data.window.state = WUHOO_WSTATE_INVALIDATED;
2131  event->data.window.data1 = window->cwidth;
2132  event->data.window.data2 = window->cheight;
2133  event->data.window.flags = WUHOO_WINDOW_FLAG_REGION_UPDATED;
2134  window->window_flags &= ~WUHOO_WINDOW_FLAG_REGION_UPDATED;
2135 
2136  return WuhooSuccess;
2137  } else if (window->window_flags & WUHOO_WINDOW_FLAG_FOCUS_LOST) {
2138  event->type = WUHOO_EVT_WINDOW;
2139  event->data.window.state = WUHOO_WSTATE_UNFOCUSED;
2140  event->data.window.data1 = window->cwidth;
2141  event->data.window.data2 = window->cheight;
2142  window->window_flags &= ~WUHOO_WINDOW_FLAG_FOCUS_LOST;
2143 
2144  return WuhooSuccess;
2145  } else if (window->window_flags & WUHOO_WINDOW_FLAG_FOCUS_GAINED) {
2146  event->type = WUHOO_EVT_WINDOW;
2147  event->data.window.state = WUHOO_WSTATE_FOCUSED;
2148  event->data.window.data1 = window->cwidth;
2149  event->data.window.data2 = window->cheight;
2150  window->window_flags &= ~WUHOO_WINDOW_FLAG_FOCUS_GAINED;
2151 
2152  return WuhooSuccess;
2153  } else if (window->window_flags & WUHOO_WINDOW_FLAG_MOVED) {
2154  event->type = WUHOO_EVT_WINDOW;
2155  event->data.window.state = WUHOO_WSTATE_MOVED;
2156  event->data.window.data1 = window->x;
2157  event->data.window.data2 = window->y;
2158  window->window_flags &= ~WUHOO_WINDOW_FLAG_MOVED;
2159 
2160  return WuhooSuccess;
2161  }
2162 
2163  WuhooBoolean needs_redrawing = (WuhooBoolean)(NSUInteger)WuhooObjMsgSend(
2164  cocoa_window->window, sel_getUid("viewsNeedDisplay"));
2165  window->window_flags |=
2166  (WuhooTrue == needs_redrawing) ? WUHOO_WINDOW_FLAG_REGION_UPDATED : 0;
2167 
2168  id pool =
2169  WuhooObjMsgSend((struct objc_object*)objc_getClass("NSAutoreleasePool"),
2170  sel_registerName("alloc"));
2171  pool = WuhooObjMsgSend(pool, sel_registerName("init"));
2172 
2173  CGPoint mouse;
2174 
2175  if (WuhooYes == _WuhooDropHandleCocoa(window, event)) {
2176  WuhooObjMsgSend(pool, sel_registerName("release"));
2177 
2178  return WuhooSuccess;
2179  }
2180 
2181  id cocoa_event =
2182  WuhooObjMsgSend(cocoa_window->app,
2183  sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"),
2184  ULONG_MAX, 0, kCFRunLoopDefaultMode, 1);
2185 
2186 
2187  NSUInteger type =
2188  (NSUInteger)WuhooObjMsgSend(cocoa_event, cocoa_window->get_type);
2189  switch (type) {
2190  /* Look int IOLLEvent.h */
2191  case NX_MOUSEENTERED:
2192  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2193  cocoa_event, sel_registerName("locationInWindow"));
2194  break;
2195  case NX_MOUSEEXITED:
2196  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2197  cocoa_event, sel_registerName("locationInWindow"));
2198  break;
2199  case kCGEventOtherMouseUp: {
2200  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2201  (struct objc_object*)objc_getClass("NSEvent"),
2202  sel_registerName("mouseLocation"));
2203  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2204  cocoa_window->window, sel_getUid("frame"));
2205  event->data.mouse_press.x = (int)mouse.x - rect.origin.x;
2206  event->data.mouse_press.y = rect.size.height - ((int)mouse.y - rect.origin.y) - cocoa_window->title_pixel_offset;
2207  event->data.mouse_press.state = WUHOO_MSTATE_MRELEASED;
2208  event->type = WUHOO_EVT_MOUSE_PRESS;
2209  } break;
2210  case kCGEventOtherMouseDown: {
2211  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2212  (struct objc_object*)objc_getClass("NSEvent"),
2213  sel_registerName("mouseLocation"));
2214  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2215  cocoa_window->window, sel_getUid("frame"));
2216 
2217  event->data.mouse_press.x = (int)mouse.x - rect.origin.x;
2218  event->data.mouse_press.y = rect.size.height - ((int)mouse.y - rect.origin.y) - cocoa_window->title_pixel_offset;
2219  event->data.mouse_press.state = WUHOO_MSTATE_MPRESSED;
2220  event->type = WUHOO_EVT_MOUSE_PRESS;
2221 
2222  } break;
2223 
2224  case kCGEventRightMouseUp: {
2225  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2226  (struct objc_object*)objc_getClass("NSEvent"),
2227  sel_registerName("mouseLocation"));
2228  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2229  cocoa_window->window, sel_getUid("frame"));
2230  event->data.mouse_press.x = (int)mouse.x - rect.origin.x;
2231  event->data.mouse_press.y = rect.size.height - ((int)mouse.y - rect.origin.y) - cocoa_window->title_pixel_offset;
2232  event->data.mouse_press.state = WUHOO_MSTATE_RRELEASED;
2233  event->type = WUHOO_EVT_MOUSE_PRESS;
2234  } break;
2235  case kCGEventRightMouseDown: {
2236  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2237  (struct objc_object*)objc_getClass("NSEvent"),
2238  sel_registerName("mouseLocation"));
2239  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2240  cocoa_window->window, sel_getUid("frame"));
2241 
2242  event->data.mouse_press.x = (int)mouse.x - rect.origin.x;
2243  event->data.mouse_press.y = rect.size.height - ((int)mouse.y - rect.origin.y) - cocoa_window->title_pixel_offset;
2244  event->data.mouse_press.state = WUHOO_MSTATE_RPRESSED;
2245  event->type = WUHOO_EVT_MOUSE_PRESS;
2246 
2247  } break;
2248  case kCGEventLeftMouseUp: {
2249  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2250  (struct objc_object*)objc_getClass("NSEvent"),
2251  sel_registerName("mouseLocation"));
2252  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2253  cocoa_window->window, sel_getUid("frame"));
2254  event->data.mouse_press.x = (int)mouse.x - rect.origin.x;
2255  event->data.mouse_press.y = rect.size.height - ((int)mouse.y - rect.origin.y) - cocoa_window->title_pixel_offset;
2256  event->data.mouse_press.state = WUHOO_MSTATE_LRELEASED;
2257  event->type = WUHOO_EVT_MOUSE_PRESS;
2258  } break;
2259  case kCGEventLeftMouseDown: {
2260  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2261  (struct objc_object*)objc_getClass("NSEvent"),
2262  sel_registerName("mouseLocation"));
2263  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2264  cocoa_window->window, sel_getUid("frame"));
2265 
2266  event->data.mouse_press.x = (int)mouse.x - rect.origin.x;
2267  event->data.mouse_press.y = rect.size.height - ((int)mouse.y - rect.origin.y) - cocoa_window->title_pixel_offset;
2268  event->data.mouse_press.state = WUHOO_MSTATE_LPRESSED;
2269  event->type = WUHOO_EVT_MOUSE_PRESS;
2270 
2271  } break;
2272  case kCGEventMouseMoved: {
2273  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2274  (struct objc_object*)objc_getClass("NSEvent"),
2275  sel_registerName("mouseLocation"));
2276  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2277  cocoa_window->window, sel_getUid("frame"));
2278 
2279  event->data.mouse_move.x = (int)mouse.x - rect.origin.x;
2280  event->data.mouse_move.y = rect.size.height - ((int)mouse.y - rect.origin.y) - cocoa_window->title_pixel_offset;
2281  event->data.mouse_move.state = WUHOO_MSTATE_UNKNOWN;
2282  event->type = WUHOO_EVT_MOUSE_MOVE;
2283 
2284  if ((WUHOO_FLAG_MOUSE_CAPTURE & window->flags) &&
2285  (window->width > (int)mouse.x && 0 <= (int)mouse.x) &&
2286  (window->height > (int)mouse.y && 0 <= (int)mouse.y)) {
2287  }
2288  } break;
2289  case kCGEventFlagsChanged:
2290  break;
2291  case kCGEventKeyDown: {
2292  key_code = (unsigned short)((NSUInteger)WuhooObjMsgSend(
2293  cocoa_event, cocoa_window->get_keycode));
2294  mod_flags =
2295  (NSUInteger)WuhooObjMsgSend(cocoa_event, cocoa_window->get_modifier_flags);
2296 
2297  WuhooObjMsgSend(pool, sel_registerName("release"));
2298 
2299  return WuhooWindowChracterDecodeCocoa(window, event, cocoa_event);
2300  } break;
2301  case kCGEventKeyUp: {
2302  key_code = (unsigned short)((NSUInteger)WuhooObjMsgSend(
2303  cocoa_event, cocoa_window->get_keycode));
2304  mod_flags =
2305  (NSUInteger)WuhooObjMsgSend(cocoa_event, cocoa_window->get_modifier_flags);
2306 
2307  event->type = WUHOO_EVT_KEY;
2308  event->data.key.state = WUHOO_KSTATE_UP;
2309  event->data.key.code = _WuhooKeyTranslateCocoa(key_code);
2310  event->data.key.mods = _WuhooKeyModifiersExtractCocoa(mod_flags);
2311  /* Mimicing SDL and GLFW here Unicode on keyup does not make sense */
2312  event->data.key.character[0] = 0;
2313 
2314  WuhooObjMsgSend(pool, sel_registerName("release"));
2315 
2316  return WuhooSuccess;
2317  } break;
2318  case kCGEventOtherMouseDragged: {
2319  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2320  (struct objc_object*)objc_getClass("NSEvent"),
2321  sel_registerName("mouseLocation"));
2322  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2323  cocoa_window->window, sel_getUid("frame"));
2324 
2325  event->data.mouse_move.x = (int)mouse.x - rect.origin.x;
2326  event->data.mouse_move.y = rect.size.height - ((int)mouse.y - rect.origin.y) - cocoa_window->title_pixel_offset;
2327  event->data.mouse_move.state = WUHOO_MSTATE_MPRESSED;
2328  event->type = WUHOO_EVT_MOUSE_MOVE;
2329  } break;
2330  case kCGEventRightMouseDragged: {
2331  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2332  (struct objc_object*)objc_getClass("NSEvent"),
2333  sel_registerName("mouseLocation"));
2334  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2335  cocoa_window->window, sel_getUid("frame"));
2336 
2337  event->data.mouse_move.x = (int)mouse.x - rect.origin.x;
2338  event->data.mouse_move.y = rect.size.height - ((int)mouse.y - rect.origin.y) - cocoa_window->title_pixel_offset;
2339  event->data.mouse_move.state = WUHOO_MSTATE_RPRESSED;
2340  event->type = WUHOO_EVT_MOUSE_MOVE;
2341 
2342  } break;
2343  case kCGEventLeftMouseDragged: {
2344  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2345  (struct objc_object*)objc_getClass("NSEvent"),
2346  sel_registerName("mouseLocation"));
2347  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2348  cocoa_window->window, sel_getUid("frame"));
2349 
2350  event->data.mouse_move.x = (int)mouse.x - rect.origin.x;
2351  event->data.mouse_move.y = rect.size.height - ((int)mouse.y - rect.origin.y) - cocoa_window->title_pixel_offset;
2352  event->data.mouse_move.state = WUHOO_MSTATE_LPRESSED;
2353  event->type = WUHOO_EVT_MOUSE_MOVE;
2354  } break;
2355  case kCGEventScrollWheel: {
2356  mouse = ((CGPoint(*)(id, SEL))WuhooObjMsgSend)(
2357  (struct objc_object*)objc_getClass("NSEvent"),
2358  sel_registerName("mouseLocation"));
2359  CGRect rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2360  cocoa_window->window, sel_getUid("frame"));
2361 
2362  event->data.mouse_wheel.x = (int)mouse.x - rect.origin.x;
2363  event->data.mouse_wheel.y = rect.size.height - ((int)mouse.y - rect.origin.y) - cocoa_window->title_pixel_offset;
2364  event->data.mouse_wheel.delta_x = ((CGFloat(*)(id, SEL))WuhooObjMsgSend)(
2365  cocoa_event, cocoa_window->get_scrolling_delta_x);
2366  event->data.mouse_wheel.delta_y = ((CGFloat(*)(id, SEL))WuhooObjMsgSend)(
2367  cocoa_event, cocoa_window->get_scrolling_delta_y);
2368  event->type = WUHOO_EVT_MOUSE_WHEEL;
2369  } break;
2370  default:
2371  break;
2372  }
2373 
2374  WuhooObjMsgSend(cocoa_window->app, sel_getUid("sendEvent:"), cocoa_event);
2375 
2376  WuhooObjMsgSend(pool, sel_registerName("release"));
2377 
2378  return WuhooSuccess;
2379 }
2380 
2381 WuhooResult
2382 _WuhooWindowShowCocoa(WuhooWindow* window)
2383 {
2384  WuhooWindowCocoa* cocoa_window;
2385 
2386  if (window == WuhooNull) {
2387  return (WuhooResult)WUHOO_STRING " : Invalid window handle.";
2388  }
2389 
2390  if (WuhooNo == window->is_initialized) {
2391  return (WuhooResult)WUHOO_STRING " : Invalid window initialization.";
2392  }
2393 
2394  cocoa_window = (WuhooWindowCocoa*)window->platform_window;
2395 
2396  WuhooObjMsgSend(cocoa_window->app, sel_registerName("deactivate"));
2397  cocoa_window->view =
2398  WuhooObjMsgSend(cocoa_window->window, sel_registerName("contentView"));
2399 
2400  WuhooObjMsgSend(cocoa_window->window, sel_registerName("makeFirstResponder:"),
2401  cocoa_window->view);
2402  WuhooObjMsgSend(cocoa_window->window, sel_registerName("makeKeyAndOrderFront:"), cocoa_window->window);
2403 
2404  WuhooObjMsgSend(cocoa_window->window, sel_registerName("orderFrontRegardless"),
2405  cocoa_window->window);
2406 
2407  WuhooObjMsgSend(cocoa_window->app, sel_getUid("setActivationPolicy:"), 0);
2408 
2409  cocoa_window->cgctx = WuhooObjMsgSend(TO_ID(objc_getClass("NSGraphicsContext")),
2410  sel_getUid("graphicsContextWithWindow:"),
2411  cocoa_window->window);
2412  if (WuhooNull == cocoa_window->cgctx) {
2413  return (WuhooResult)WUHOO_PLATFORM_API_STRING
2414  " : failed to get NSGraphicsContext of window.";
2415  }
2416 
2417 #ifdef WUHOO_OPENGL_ENABLE
2418  if (!WuhooOnlySet(window->flags, WUHOO_FLAG_OPENGL)) {
2419  return WuhooSuccess;
2420  }
2421 
2422  CGLPixelFormatAttribute gl_attrs[64];
2423  int gl_index = 0;
2424  //gl_attrs[gl_index++] = kCGLPFAAccelerated;
2425  if (WuhooTrue == window->gl_framebuffer.doublebuffer)
2426  gl_attrs[gl_index++] = kCGLPFADoubleBuffer;
2427  /* deprecated */ // if (WuhooTrue == window->gl_framebuffer.stereo)
2428  // gl_attrs[gl_index++] = kCGLPFAStereo;
2429  gl_attrs[gl_index++] = kCGLPFASampleBuffers;
2430  gl_attrs[gl_index++] = window->gl_framebuffer.samples;
2431  gl_attrs[gl_index++] = kCGLPFAAuxBuffers;
2432  gl_attrs[gl_index++] = window->gl_framebuffer.auxBuffers;
2433  gl_attrs[gl_index++] = kCGLPFAColorSize;
2434  gl_attrs[gl_index++] = window->gl_framebuffer.redBits +
2435  window->gl_framebuffer.greenBits +
2436  window->gl_framebuffer.blueBits;
2437  gl_attrs[gl_index++] = kCGLPFADepthSize;
2438  gl_attrs[gl_index++] = window->gl_framebuffer.depthBits;
2439  gl_attrs[gl_index++] = kCGLPFAAlphaSize;
2440  gl_attrs[gl_index++] = window->gl_framebuffer.alphaBits;
2441  gl_attrs[gl_index++] = kCGLPFAStencilSize;
2442  gl_attrs[gl_index++] = window->gl_framebuffer.stencilBits;
2443  gl_attrs[gl_index++] = kCGLPFAAccumSize;
2444  gl_attrs[gl_index++] = window->gl_framebuffer.accumRedBits +
2445  window->gl_framebuffer.accumGreenBits +
2446  window->gl_framebuffer.accumBlueBits;
2447  gl_attrs[gl_index++] = kCGLPFAOpenGLProfile;
2448  if (window->gl_framebuffer.version.major >= 3)
2449  gl_attrs[gl_index++] = (CGLPixelFormatAttribute)kCGLOGLPVersion_GL3_Core;
2450  else if (window->gl_framebuffer.version.major >= 4)
2451  gl_attrs[gl_index++] = (CGLPixelFormatAttribute)kCGLOGLPVersion_GL4_Core;
2452  else
2453  gl_attrs[gl_index++] = (CGLPixelFormatAttribute)kCGLOGLPVersion_Legacy;
2454  gl_attrs[gl_index++] = (CGLPixelFormatAttribute)0;
2455 
2456  CGLPixelFormatObj pix;
2457  GLint npix = 0;
2458  CGLError cgl_error = CGLChoosePixelFormat(gl_attrs, &pix, &npix);
2459  if (0 != cgl_error) {
2460  return (WuhooResult)WUHOO_PLATFORM_API_STRING
2461  " : CGLChoosePixelFormat failed.";
2462  }
2463 
2464  CGLContextObj ctx;
2465  cgl_error = CGLCreateContext(pix, NULL, &ctx);
2466  if (0 != cgl_error) {
2467  return (WuhooResult)WUHOO_PLATFORM_API_STRING " : CGLCreateContext failed.";
2468  }
2469 
2470  GLint dim[2];
2471  dim[0] = window->width;
2472  dim[1] = window->height;
2473  id gl_ctx =
2474  WuhooObjMsgSend(TO_ID(objc_getClass("NSOpenGLContext")), sel_getUid("alloc"));
2475  gl_ctx = WuhooObjMsgSend(gl_ctx, sel_getUid("initWithCGLContextObj:"), ctx);
2476 
2477  WuhooObjMsgSend(gl_ctx, sel_getUid("setView:"), cocoa_window->view);
2478  WuhooObjMsgSend(gl_ctx, sel_getUid("makeCurrentContext"));
2479  /* Set this t suppress high dpi mechanisms */
2480  WuhooObjMsgSend(cocoa_window->view, sel_getUid("setWantsBestResolutionOpenGLSurface:"), 0);
2481  WuhooObjMsgSend(gl_ctx, sel_getUid("update"));
2482  WuhooObjMsgSend(gl_ctx, sel_getUid("flushBuffer"));
2483 
2484  cocoa_window->glctx = gl_ctx;
2485 #endif
2486 
2487  return WuhooSuccess;
2488 }
2489 
2490 WuhooResult
2491 _WuhooWindowCreateCocoa(WuhooWindow* window, int posx, int posy,
2492  WuhooSize width, WuhooSize height, const char* title,
2493  WuhooFlags flags)
2494 {
2495  /* TODO: Size Checks */
2496  WuhooWindowCocoa* cocoa_window;
2497 
2498  cocoa_window = (WuhooWindowCocoa*)&window->memory[0];
2499 
2500  cocoa_window->WuhooNSView = objc_allocateClassPair((Class)objc_getClass("NSView"), "WuhooNSView", 0);
2501  cocoa_window->WuhooNSWindow = objc_allocateClassPair((Class)objc_getClass("NSWindow"), "WuhooNSWindow", 0);
2502 
2503  /* https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100 */
2504  class_addMethod(cocoa_window->WuhooNSView, sel_getUid("drawRect:"), (IMP) View_drawRect, "v@:{CGRect={CGPoint=dd}{CGSize=dd}}");
2505  class_addMethod(cocoa_window->WuhooNSWindow, sel_getUid("constrainFrameRect:toScreen:"), (IMP) Window_constrainFrameRect, "{CGRect={CGPoint=dd}{CGSize=dd}}@:{CGRect={CGPoint=dd}{CGSize=dd}}^v");
2506 
2507  class_addIvar(cocoa_window->WuhooNSView, "_wheels", sizeof(void*), log2(sizeof(void*)), "^v");
2508  class_addIvar(cocoa_window->WuhooNSView, "_wheels", sizeof(void*), log2(sizeof(void*)), "^v");
2509 
2510  objc_registerClassPair(cocoa_window->WuhooNSView);
2511  objc_registerClassPair(cocoa_window->WuhooNSWindow);
2512 
2513  window->is_alive = WuhooYes;
2514  window->global_mods = (WuhooKeyModifiers)0;
2515 
2516  cocoa_window->client_offset = 0;
2517  cocoa_window->app = (struct objc_object*)WuhooNull;
2518  cocoa_window->window = (struct objc_object*)WuhooNull;
2519  cocoa_window->glctx = (struct objc_object*)WuhooNull;
2520  cocoa_window->cgctx = (struct objc_object*)WuhooNull;
2521  cocoa_window->view = (struct objc_object*)WuhooNull;
2522  cocoa_window->image = (CGImageRef)WuhooNull;
2523  cocoa_window->pool = (struct objc_object*)WuhooNull;
2524 
2525  /* Gather global state */
2526  window->global_mods |= (WuhooKeyModifiers)(
2527  (CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState) &
2528  NX_ALPHASHIFTMASK)
2529  ? (WuhooKeyModifiers)WUHOO_KMOD_CAPS
2530  : WUHOO_KMOD_NONE);
2531  window->global_mods |= (WuhooKeyModifiers)(
2532  (CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState) &
2533  NX_SECONDARYFNMASK)
2534  ? (WuhooKeyModifiers)WUHOO_KMOD_MODE
2535  : WUHOO_KMOD_NONE);
2536 
2537  id screen = WuhooObjMsgSend((struct objc_object*)objc_getClass("NSScreen"),
2538  sel_registerName("mainScreen"));
2539 
2540  CGRect screen_rect = ((CGRect(*)(id, SEL))WuhooObjMsgSendStret)(
2541  screen, sel_registerName("visibleFrame"));
2542 
2543  cocoa_window->pool =
2544  WuhooObjMsgSend((struct objc_object*)objc_getClass("NSAutoreleasePool"),
2545  sel_registerName("alloc"));
2546  cocoa_window->pool =
2547  WuhooObjMsgSend(cocoa_window->pool, sel_registerName("init"));
2548  if (WuhooNull == cocoa_window->pool) {
2549  return (WuhooResult)WUHOO_PLATFORM_API_STRING
2550  " : NSAutoreleasePool failed.";
2551  }
2552 
2553  cocoa_window->app =
2554  WuhooObjMsgSend((struct objc_object*)objc_getClass("NSApplication"),
2555  sel_getUid("sharedApplication"));
2556  if (WuhooNull == cocoa_window->app) {
2557  return (WuhooResult)WUHOO_PLATFORM_API_STRING
2558  " : NSApplication sharedApplication failed.";
2559  }
2560 
2561  CGRect test;
2562  test.origin.x = 0;
2563  test.origin.y = 0;
2564  test.size.width = width;
2565  test.size.height = height;
2566 
2567  cocoa_window->previous_change_count = -1;
2568  cocoa_window->window_mask = WuhooNSWindowStyleMaskMiniaturizable;
2569  cocoa_window->window_mask |=
2570  !(WUHOO_FLAG_BORDERLESS & flags) ? WuhooNSWindowStyleMaskTitled : 0;
2571  cocoa_window->window_mask |=
2572  (WUHOO_FLAG_RESIZEABLE & flags) ? WuhooNSWindowStyleMaskResizable : 0;
2573  cocoa_window->window_mask |=
2574  (WUHOO_FLAG_CLOSEABLE & flags) ? WuhooNSWindowStyleMaskClosable : 0;
2575 
2576  CGRect client_rect =
2577  ((CGRect(*)(id, SEL, CGRect, unsigned int))WuhooObjMsgSendStret)(
2578  (struct objc_object*)objc_getClass("NSWindow"),
2579  sel_registerName("contentRectForFrameRect:styleMask:"), test,
2580  (unsigned int)cocoa_window->window_mask);
2581  int title_height_diff = (int)(height - client_rect.size.height);
2582  CGFloat dock_height_diff = screen_rect.size.height - height;
2583  CGRect rect;
2584  rect.size.width = width;
2585  rect.size.height = height;
2586 
2587  if (dock_height_diff < 0.0f) {
2588  cocoa_window->client_offset = ((int)((dock_height_diff + 0.5f)));
2589  cocoa_window->client_offset +=
2590  (title_height_diff < 0.0f) ? ((int)((title_height_diff + 0.5f))) : 0;
2591  }
2592 
2593  cocoa_window->title_pixel_offset = title_height_diff;
2594 
2595  window->width = rect.size.width;
2596  window->height = rect.size.height;
2597 
2598  rect.size.height -=
2599  (WUHOO_FLAG_CLIENT_REGION & flags) ? 0 : title_height_diff;
2600 
2601  rect.origin.x = (WuhooDefaultPosition != posx)
2602  ? posx
2603  : ((screen_rect.size.width / 2) - (rect.size.width / 2));
2604  rect.origin.y = (WuhooDefaultPosition != posy)
2605  ? posy
2606  : ((screen_rect.size.height / 2) - (rect.size.height / 2));
2607 
2608  cocoa_window->window = WuhooObjMsgSend(
2609  (struct objc_object*)objc_getClass("WuhooNSWindow"), sel_registerName("alloc"));
2610  cocoa_window->window = WuhooObjMsgSend(
2611  cocoa_window->window,
2612  sel_registerName("initWithContentRect:styleMask:backing:defer:"), rect,
2613  cocoa_window->window_mask, 1, WuhooNo);
2614  if (WuhooNull == cocoa_window->window) {
2615  return (WuhooResult)WUHOO_PLATFORM_API_STRING
2616  " : NSWindow initWithContentRect failed.";
2617  }
2618 
2619  CGRect view_rect = CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height);
2620  id view = WuhooObjMsgSend(WuhooObjMsgSend((id)objc_getClass("WuhooNSView"), sel_getUid("alloc")), sel_getUid("initWithFrame:"), view_rect);
2621 
2622  // here we simply add the view to the window.
2623  WuhooObjMsgSend(cocoa_window->window, sel_getUid("setContentView:"), view);
2624  WuhooObjMsgSend(cocoa_window->window, sel_getUid("becomeFirstResponder"));
2625 
2626  Ivar ivar = class_getInstanceVariable(cocoa_window->WuhooNSView, "_wheels");
2627  object_setIvar(view, ivar, (id)window);
2628 
2629  window->cwidth = rect.size.width;
2630  window->cheight = rect.size.height;
2631 
2632  id typ = WuhooObjMsgSend((struct objc_object*)objc_getClass("NSArray"),
2633  sel_registerName("arrayWithObjects:"), kUTTypeURL,
2634  kUTTypeFileURL, WuhooNull);
2635  WuhooObjMsgSend(cocoa_window->window,
2636  sel_registerName("registerForDraggedTypes:"), typ);
2637 
2638  if ((WUHOO_FLAG_TITLED & flags) && title) {
2639  /* TODO: Check for reasonable title length??? */
2640  CFStringRef window_title =
2641  CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8);
2642  WuhooObjMsgSend(cocoa_window->window, sel_registerName("setTitle:"),
2643  window_title);
2644  CFRelease(window_title);
2645  }
2646 
2647  CFNotificationCenterAddObserver(
2648  CFNotificationCenterGetLocalCenter(), (const void*)window,
2649  _WuhooNotificationCenterCallback, (CFStringRef)WuhooNull,
2650  cocoa_window->window, CFNotificationSuspensionBehaviorDeliverImmediately);
2651 
2652  window->platform_window = cocoa_window;
2653 
2654  cocoa_window->get_type = sel_registerName("type");
2655  cocoa_window->get_subtype = sel_registerName("subtype");
2656  cocoa_window->get_clickcount = sel_registerName("clickCount");
2657  cocoa_window->get_characters = sel_registerName("characters");
2658  cocoa_window->get_modifier_flags = sel_registerName("modifierFlags");
2659  cocoa_window->get_keycode = sel_registerName("keyCode");
2660  cocoa_window->get_scrolling_delta_x = sel_registerName("scrollingDeltaX");
2661  cocoa_window->get_scrolling_delta_y = sel_registerName("scrollingDeltaY");
2662  cocoa_window->get_delta_x = sel_registerName("deltaX");
2663  cocoa_window->get_delta_y = sel_registerName("deltaY");
2664 
2665  window->window_flags |= WUHOO_WINDOW_FLAG_RESIZED;
2666 
2667 
2668  WuhooObjMsgSend(cocoa_window->app,
2669  sel_getUid("finishLaunching"));
2670 
2671  return WuhooSuccess;
2672 }
2673 
2674 #endif
2675 
2676 #ifdef _WIN32
2677 
2678 #define WUHOO_MAX_ENCODED_TITLE_LENGTH 512
2679 
2680 #include <Windows.h>
2681 #include <shellapi.h>
2682 
2683 #define WuhooMaybeUnused
2684 
2685 typedef HGLRC WINAPI
2686  wglCreateContextAttribsARB_type(HDC hdc, HGLRC hShareContext,
2687  const int* attribList);
2688 wglCreateContextAttribsARB_type* WUHOO_wglCreateContextAttribsARB;
2689 
2690 // See https://www.opengl.org/registry/specs/ARB/wgl_create_context.txt for all
2691 // values
2692 #define WUHOO_WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
2693 #define WUHOO_WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
2694 #define WUHOO_WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
2695 
2696 #define WUHOO_WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
2697 
2698 typedef BOOL WINAPI
2699  wglChoosePixelFormatARB_type(HDC hdc, const int* piAttribIList,
2700  const FLOAT* pfAttribFList, UINT nMaxFormats,
2701  int* piFormats, UINT* nNumFormats);
2702 wglChoosePixelFormatARB_type* WUHOO_wglChoosePixelFormatARB;
2703 
2704 #define WUHOO_WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
2705 #define WUHOO_WGL_DRAW_TO_WINDOW_ARB 0x2001
2706 #define WUHOO_WGL_DRAW_TO_BITMAP_ARB 0x2002
2707 #define WUHOO_WGL_ACCELERATION_ARB 0x2003
2708 #define WUHOO_WGL_NEED_PALETTE_ARB 0x2004
2709 #define WUHOO_WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
2710 #define WUHOO_WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
2711 #define WUHOO_WGL_SWAP_METHOD_ARB 0x2007
2712 #define WUHOO_WGL_NUMBER_OVERLAYS_ARB 0x2008
2713 #define WUHOO_WGL_NUMBER_UNDERLAYS_ARB 0x2009
2714 #define WUHOO_WGL_TRANSPARENT_ARB 0x200A
2715 #define WUHOO_WGL_SHARE_DEPTH_ARB 0x200C
2716 #define WUHOO_WGL_SHARE_STENCIL_ARB 0x200D
2717 #define WUHOO_WGL_SHARE_ACCUM_ARB 0x200E
2718 #define WUHOO_WGL_SUPPORT_GDI_ARB 0x200F
2719 #define WUHOO_WGL_SUPPORT_OPENGL_ARB 0x2010
2720 #define WUHOO_WGL_DOUBLE_BUFFER_ARB 0x2011
2721 #define WUHOO_WGL_STEREO_ARB 0x2012
2722 #define WUHOO_WGL_PIXEL_TYPE_ARB 0x2013
2723 #define WUHOO_WGL_COLOR_BITS_ARB 0x2014
2724 #define WUHOO_WGL_RED_BITS_ARB 0x2015
2725 #define WUHOO_WGL_RED_SHIFT_ARB 0x2016
2726 #define WUHOO_WGL_GREEN_BITS_ARB 0x2017
2727 #define WUHOO_WGL_GREEN_SHIFT_ARB 0x2018
2728 #define WUHOO_WGL_BLUE_BITS_ARB 0x2019
2729 #define WUHOO_WGL_BLUE_SHIFT_ARB 0x201A
2730 #define WUHOO_WGL_ALPHA_BITS_ARB 0x201B
2731 #define WUHOO_WGL_ALPHA_SHIFT_ARB 0x201C
2732 #define WUHOO_WGL_ACCUM_BITS_ARB 0x201D
2733 #define WUHOO_WGL_ACCUM_RED_BITS_ARB 0x201E
2734 #define WUHOO_WGL_ACCUM_GREEN_BITS_ARB 0x201F
2735 #define WUHOO_WGL_ACCUM_BLUE_BITS_ARB 0x2020
2736 #define WUHOO_WGL_ACCUM_ALPHA_BITS_ARB 0x2021
2737 #define WUHOO_WGL_DEPTH_BITS_ARB 0x2022
2738 #define WUHOO_WGL_STENCIL_BITS_ARB 0x2023
2739 #define WUHOO_WGL_AUX_BUFFERS_ARB 0x2024
2740 #define WUHOO_WGL_NO_ACCELERATION_ARB 0x2025
2741 #define WUHOO_WGL_GENERIC_ACCELERATION_ARB 0x2026
2742 #define WUHOO_WGL_FULL_ACCELERATION_ARB 0x2027
2743 #define WUHOO_WGL_SWAP_EXCHANGE_ARB 0x2028
2744 #define WUHOO_WGL_SWAP_COPY_ARB 0x2029
2745 #define WUHOO_WGL_SWAP_UNDEFINED_ARB 0x202A
2746 #define WUHOO_WGL_TYPE_RGBA_ARB 0x202B
2747 #define WUHOO_WGL_TYPE_COLORINDEX_ARB 0x202C
2748 #define WUHOO_WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
2749 #define WUHOO_WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
2750 #define WUHOO_WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
2751 #define WUHOO_WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
2752 #define WUHOO_WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
2753 
2754 typedef struct
2755 {
2756  HINSTANCE app;
2757  HWND window;
2758  HDC dc;
2759 #ifdef WUHOO_OPENGL_ENABLE
2760  HGLRC glrc;
2761 #endif
2762  HDC mem_dc;
2763  HBITMAP bitmap;
2764  HDROP last_drop; /* last drop */
2765  WuhooRGBA* dib;
2766  WuhooSize last_click;
2767 } WuhooWindowWin32;
2768 
2769 WuhooResult
2770 _WuhooWindowSetTitleWin32(WuhooWindow* window, const char* title)
2771 {
2772  WuhooWindowWin32* win32_window = (WuhooWindowWin32*)window->platform_window;
2773  WuhooResult result = WuhooSuccess;
2774 
2775 #ifdef WUHOO_UNICODE
2776  WuhooUTF16 encoded_title[WUHOO_MAX_ENCODED_TITLE_LENGTH];
2777  WuhooUTF16* encoded_title_ptr = &encoded_title[0];
2778 
2779  WuhooMemzero(encoded_title, sizeof(encoded_title));
2780  size_t title_length = WuhooStringLength(title, WUHOO_MAX_TITLE_LENGTH);
2781  WuhooConvertUTF8toUTF16((const WuhooUTF8 **)&title, (const WuhooUTF8 *)title + title_length, &encoded_title_ptr,
2782  encoded_title_ptr + WUHOO_MAX_ENCODED_TITLE_LENGTH,
2783  WuhooStrictConversion);
2784 #else
2785  const WuhooUTF8* encoded_title = (const WuhooUTF8*)title;
2786 #endif
2787 
2788  SetWindowText(win32_window->window, (LPTSTR)encoded_title);
2789 
2790  return result;
2791 }
2792 
2793 WuhooResult
2794 _WuhooWindowDestroyWin32(WuhooWindow* window)
2795 {
2796  WuhooWindowWin32* win32_window = (WuhooWindowWin32*)window->platform_window;
2797  WuhooResult result = WuhooSuccess;
2798 
2799  if (WuhooNull != win32_window->last_drop)
2800  DragFinish(win32_window->last_drop);
2801 
2802  TCHAR classname[32];
2803  GetClassName(win32_window->window, classname, 32);
2804  ReleaseDC(win32_window->window, win32_window->dc);
2805  DestroyWindow(win32_window->window);
2806  UnregisterClass(classname, win32_window->app);
2807 
2808 #ifdef WUHOO_OPENGL_ENABLE
2809  wglMakeCurrent(win32_window->dc, 0);
2810  wglDeleteContext(win32_window->glrc);
2811 #endif
2812 
2813  return result;
2814 }
2815 
2816 WuhooResult
2817 _WuhooWindowRegionGetWin32(WuhooWindow* window, int* posx, int* posy,
2818  WuhooSize* width, WuhooSize* height)
2819 {
2820  WuhooResult result = WuhooSuccess;
2821 
2822  WuhooWindowWin32* win32_window = (WuhooWindowWin32*)window->platform_window;
2823 
2824  RECT rect;
2825  GetWindowRect(win32_window->window, &rect);
2826 
2827  if (WuhooNull != posx)
2828  *posx = rect.left;
2829  if (WuhooNull != posy)
2830  *posy = rect.top;
2831  if (WuhooNull != width)
2832  *width = rect.right - rect.left;
2833  if (WuhooNull != height)
2834  *height = rect.bottom - rect.top;
2835 
2836  return result;
2837 }
2838 
2839 WuhooResult
2840 _WuhooWindowRegionSetWin32(WuhooWindow* window, int posx, int posy, int width,
2841  int height)
2842 {
2843  WuhooResult result = WuhooSuccess;
2844 
2845  WuhooWindowWin32* win32_window = (WuhooWindowWin32*)window->platform_window;
2846 
2847  MoveWindow(win32_window->window, posx, posy, width, height, TRUE);
2848  UpdateWindow(win32_window->window);
2849  window->window_flags |= WUHOO_WINDOW_FLAG_RESIZED;
2850 
2851  RECT rect;
2852  GetClientRect(win32_window->window, &rect);
2853 
2854  window->x = rect.left;
2855  window->y = rect.top;
2856  window->width = rect.right;
2857  window->height = rect.bottom;
2858 
2859  return result;
2860 }
2861 
2862 WuhooResult
2863 _WuhooWindowClientRegionGetWin32(WuhooWindow* window, int* posx, int* posy,
2864  WuhooSize* width, WuhooSize* height)
2865 {
2866  WuhooResult result = WuhooSuccess;
2867 
2868  WuhooWindowWin32* win32_window = (WuhooWindowWin32*)window->platform_window;
2869 
2870  RECT rect;
2871  GetClientRect(win32_window->window, &rect);
2872 
2873  if (WuhooNull != posx)
2874  *posx = rect.left;
2875  if (WuhooNull != posy)
2876  *posy = rect.top;
2877  if (WuhooNull != width)
2878  *width = rect.right - rect.left;
2879  if (WuhooNull != height)
2880  *height = rect.bottom - rect.top;
2881 
2882  return result;
2883 }
2884 
2885 WuhooResult
2886 _WuhooWindowClientRegionSetWin32(WuhooWindow* window, int posx, int posy,
2887  WuhooSize width, WuhooSize height)
2888 {
2889  WuhooResult result = WuhooSuccess;
2890 
2891  WuhooWindowWin32* win32_window = (WuhooWindowWin32*)window->platform_window;
2892 
2893  RECT rect = { 0, 0, (LONG)width, (LONG)height };
2894  DWORD dwStyle = (DWORD)GetWindowLong(win32_window->window, GWL_STYLE);
2895  AdjustWindowRect(&rect, dwStyle, FALSE); // adjust the size
2896 
2897  MoveWindow(win32_window->window, posx, posy, rect.right - rect.left,
2898  rect.bottom - rect.top, TRUE);
2899  UpdateWindow(win32_window->window);
2900  window->window_flags |= WUHOO_WINDOW_FLAG_RESIZED;
2901 
2902  window->x = posx;
2903  window->y = posy;
2904  window->width = rect.right - rect.left;
2905  window->height = rect.bottom - rect.top;
2906 
2907  return WuhooSuccess;
2908 }
2909 
2910 WuhooResult
2911 _WuhooWindowBlitWin32(WuhooWindow* window, WuhooRGBA* pixels, WuhooSize x,
2912  WuhooSize y, WuhooSize width, WuhooSize height)
2913 {
2914  WuhooResult result = WuhooSuccess;
2915 
2916  WuhooWindowWin32* win32_window = (WuhooWindowWin32*)window->platform_window;
2917 
2918 #ifdef WUHOO_OPENGL_ENABLE
2919  if (WUHOO_FLAG_OPENGL & window->flags) {
2920  SwapBuffers(win32_window->dc);
2921  return WuhooSuccess;
2922  }
2923 #endif /* WUHOO_OPENGL_ENABLE */
2924 
2925  if (WUHOO_WINDOW_FLAG_CLOSED & window->window_flags ||
2926  WUHOO_WINDOW_FLAG_RESIZED & window->window_flags ||
2927  WUHOO_WINDOW_FLAG_REGION_UPDATED & window->window_flags) {
2928  /* If important window states have not been handled yet
2929  * don not proceed with blitting because this might
2930  * cause inconcistent behaviors. **REVISIT**
2931  */
2932  return WuhooSuccess;
2933  }
2934 
2935  RECT rect;
2936  GetClientRect(win32_window->window, &rect);
2937 
2938  /*BITMAPINFO bmi;
2939  bmi.bmiHeader.biSize = sizeof(BITMAPINFO);
2940  bmi.bmiHeader.biWidth = rect.right;
2941  bmi.bmiHeader.biHeight = -rect.bottom; // Order pixels from top to bottom
2942  bmi.bmiHeader.biPlanes = 1;
2943  bmi.bmiHeader.biBitCount = 32; // last byte not used, 32 bit for alignment
2944  bmi.bmiHeader.biCompression = BI_RGB;
2945  bmi.bmiHeader.biSizeImage = 0;
2946  bmi.bmiHeader.biXPelsPerMeter = 0;
2947  bmi.bmiHeader.biYPelsPerMeter = 0;
2948  bmi.bmiHeader.biClrUsed = 0;
2949  bmi.bmiHeader.biClrImportant = 0;
2950  bmi.bmiColors[0].rgbBlue = 0;
2951  bmi.bmiColors[0].rgbGreen = 0;
2952  bmi.bmiColors[0].rgbRed = 0;
2953  bmi.bmiColors[0].rgbReserved = 0;
2954 
2955  HRESULT iRet = StretchDIBits(win32_window->dc,
2956  // destination rectangle
2957  0, 0, rect.right, rect.bottom,
2958  // source rectangle
2959  0, 0, rect.right, rect.bottom,
2960  pixels,
2961  &bmi,
2962  DIB_RGB_COLORS,
2963  SRCCOPY);*/
2964 
2965  window->convert_rgba(win32_window->dib, pixels, x, y, window->cwidth,
2966  window->cheight, window->cwidth, window->cheight);
2967 
2968  SelectObject(win32_window->mem_dc, win32_window->bitmap);
2969  BitBlt(win32_window->dc, 0, 0, rect.right, rect.bottom, win32_window->mem_dc,
2970  0, 0, SRCCOPY);
2971  // SelectObject( hdcMem, hbmOld );
2972 
2973  return result;
2974 }
2975 
2976 WuhooResult
2977 _WuhooWindowDropContentsGetWin32(WuhooWindow* window, WuhooEvent* event,
2978  char* buffer, int buffer_size)
2979 {
2980  WuhooChar scratch_buffer[WUHOO_MAX_FILE_NAME_LENGTH];
2981 
2982  WuhooZeroInit(scratch_buffer, sizeof(scratch_buffer));
2983  WuhooZeroInit(buffer, buffer_size);
2984 
2985  WuhooUTF8* data_ptr = (WuhooUTF8*)buffer;
2986  for (WuhooSize drop_index = 0; drop_index < event->data.drop.count; drop_index++) {
2987  const WuhooChar* buffer_ptr = &scratch_buffer[0];
2988  UINT written = DragQueryFile(
2989  (HDROP)event->data.drop.context, drop_index, (TCHAR*)buffer_ptr,
2990  sizeof(scratch_buffer) / sizeof(scratch_buffer[0]));
2991 #ifdef WUHOO_UNICODE
2992  WuhooConversionResult res =
2993  WuhooConvertUTF16toUTF8(&buffer_ptr, buffer_ptr + written, &data_ptr,
2994  data_ptr + buffer_size, WuhooStrictConversion);
2995  *data_ptr++ = '\n';
2996 #else
2997  WuhooStringCopy((char*)data_ptr, (const char*)buffer_ptr, written);
2998  data_ptr += written;
2999  *data_ptr++ = '\n';
3000 #endif
3001  }
3002 
3003  *data_ptr = 0;
3004 
3005  // DragFinish((HDROP)event->data.drop.context);
3006 
3007  return WuhooSuccess;
3008 }
3009 
3010 WuhooResult
3011 _WuhooWindowEventNextWin32(WuhooWindow* window, WuhooEvent* event)
3012 {
3013  WuhooWindowWin32* win32_window = (WuhooWindowWin32*)window->platform_window;
3014  int mods = WUHOO_KMOD_NONE;
3015  MSG msg = { 0 };
3016  TCHAR ch;
3017  TCHAR szBuf[128];
3018  WuhooBoolean WasDown;
3019 
3020  /* @see
3021  * https://www.daniweb.com/programming/software-development/code/241875/fast-animation-with-the-windows-gdi
3022  */
3023  while (1) {
3024  // if( PeekMessage( &msg, win32_window->window, 0, 0, PM_REMOVE ) ) {
3025  if (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE)) {
3026  switch (msg.message) {
3027  case WM_DROPFILES: {
3028  HDROP hdrop;
3029  hdrop = (HDROP)msg.wParam;
3030  if (WuhooNull != win32_window->last_drop)
3031  DragFinish(win32_window->last_drop);
3032  win32_window->last_drop = hdrop;
3033 
3034  int drop_count =
3035  DragQueryFile(hdrop, 0xFFFFFFFF, (TCHAR*)WuhooNull, 0);
3036  int drop_size = 0;
3037  int drop_max_size = 0;
3038  int drop_total_size = 0;
3039  int written = 0;
3040  for (int drop_index = 0; drop_index < drop_count; drop_index++) {
3041  drop_size =
3042  DragQueryFile(hdrop, drop_index, (TCHAR*)WuhooNull, 0) + 1;
3043  drop_total_size += drop_size;
3044  if (drop_size > drop_max_size)
3045  drop_max_size = drop_size;
3046  }
3047 
3048  event->data.drop.count = drop_count;
3049  event->data.drop.size = drop_total_size * 4;
3050  event->data.drop.context = (WuhooHandle)hdrop;
3051  event->type = WUHOO_EVT_DROP;
3052  } break;
3053  case WM_MOUSEMOVE: {
3054  event->type = WUHOO_EVT_MOUSE_MOVE;
3055  event->data.mouse_move.x = LOWORD(msg.lParam);
3056  event->data.mouse_move.y = HIWORD(msg.lParam);
3057  event->data.mouse_move.state = (msg.wParam & MK_LBUTTON)
3058  ? WUHOO_MSTATE_LPRESSED
3059  : WUHOO_MSTATE_UNKNOWN;
3060  event->data.mouse_move.state = (msg.wParam & MK_MBUTTON)
3061  ? WUHOO_MSTATE_MPRESSED
3062  : event->data.mouse_move.state;
3063  event->data.mouse_move.state = (msg.wParam & MK_RBUTTON)
3064  ? WUHOO_MSTATE_RPRESSED
3065  : event->data.mouse_move.state;
3066  event->data.mouse_move.mods |=
3067  (msg.wParam & MK_CONTROL) ? WUHOO_KMOD_CTRL : WUHOO_KMOD_NONE;
3068  event->data.mouse_move.mods |=
3069  (msg.wParam & MK_SHIFT) ? WUHOO_KMOD_SHIFT : WUHOO_KMOD_NONE;
3070 
3071  } break;
3072  case WM_MOUSEWHEEL: {
3073  event->type = WUHOO_EVT_MOUSE_WHEEL;
3074  event->data.mouse_wheel.delta_y =
3075  (float)GET_WHEEL_DELTA_WPARAM(msg.wParam) / WHEEL_DELTA;
3076  }break;
3077  case WM_MBUTTONUP: {
3078  event->type = WUHOO_EVT_MOUSE_PRESS;
3079  event->data.mouse_press.state = WUHOO_MSTATE_MRELEASED;
3080  event->data.mouse_press.mods |=
3081  (msg.wParam & MK_CONTROL) ? WUHOO_KMOD_CTRL : WUHOO_KMOD_NONE;
3082  event->data.mouse_press.mods |=
3083  (msg.wParam & MK_SHIFT) ? WUHOO_KMOD_SHIFT : WUHOO_KMOD_NONE;
3084 
3085  if (win32_window->window == GetCapture())
3086  ReleaseCapture();
3087  } break;
3088  case WM_MBUTTONDBLCLK:
3089  event->data.mouse_press.click_count++;
3090  /* fallthrough */
3091  case WM_MBUTTONDOWN: {
3092  event->type = WUHOO_EVT_MOUSE_PRESS;
3093  event->data.mouse_press.state = WUHOO_MSTATE_MPRESSED;
3094  event->data.mouse_press.mods |=
3095  (msg.wParam & MK_CONTROL) ? WUHOO_KMOD_CTRL : WUHOO_KMOD_NONE;
3096  event->data.mouse_press.mods |=
3097  (msg.wParam & MK_SHIFT) ? WUHOO_KMOD_SHIFT : WUHOO_KMOD_NONE;
3098 
3099  if (win32_window->window != GetCapture())
3100  SetCapture(win32_window->window);
3101  break;
3102  case WM_LBUTTONUP:
3103  event->type = WUHOO_EVT_MOUSE_PRESS;
3104  event->data.mouse_press.state = WUHOO_MSTATE_LRELEASED;
3105  event->data.mouse_press.mods |=
3106  (msg.wParam & MK_CONTROL) ? WUHOO_KMOD_CTRL : WUHOO_KMOD_NONE;
3107  event->data.mouse_press.mods |=
3108  (msg.wParam & MK_SHIFT) ? WUHOO_KMOD_SHIFT : WUHOO_KMOD_NONE;
3109 
3110  if (win32_window->window == GetCapture())
3111  ReleaseCapture();
3112  } break;
3113  case WM_LBUTTONDOWN:
3114  /*if (GetTickCount() - win32_window->last_click <
3115  GetDoubleClickTime()) { event->data.mouse_press.click_count++;
3116  }
3117  win32_window->last_click = GetTickCount();*/
3118  event->type = WUHOO_EVT_MOUSE_PRESS;
3119  event->data.mouse_press.state = WUHOO_MSTATE_LPRESSED;
3120  event->data.mouse_press.mods |=
3121  (msg.wParam & MK_CONTROL) ? WUHOO_KMOD_CTRL : WUHOO_KMOD_NONE;
3122  event->data.mouse_press.mods |=
3123  (msg.wParam & MK_SHIFT) ? WUHOO_KMOD_SHIFT : WUHOO_KMOD_NONE;
3124  event->data.mouse_press.click_count++;
3125 
3126  if (win32_window->window != GetCapture())
3127  SetCapture(win32_window->window);
3128  break;
3129  case WM_RBUTTONUP:
3130  event->type = WUHOO_EVT_MOUSE_PRESS;
3131  event->data.mouse_press.state = WUHOO_MSTATE_RRELEASED;
3132  event->data.mouse_press.mods |=
3133  (msg.wParam & MK_CONTROL) ? WUHOO_KMOD_CTRL : WUHOO_KMOD_NONE;
3134  event->data.mouse_press.mods |=
3135  (msg.wParam & MK_SHIFT) ? WUHOO_KMOD_SHIFT : WUHOO_KMOD_NONE;
3136 
3137  if (win32_window->window == GetCapture())
3138  ReleaseCapture();
3139  break;
3140  case WM_RBUTTONDOWN:
3141  event->type = WUHOO_EVT_MOUSE_PRESS;
3142  event->data.mouse_press.state = WUHOO_MSTATE_RPRESSED;
3143  event->data.mouse_press.mods |=
3144  (msg.wParam & MK_CONTROL) ? WUHOO_KMOD_CTRL : WUHOO_KMOD_NONE;
3145  event->data.mouse_press.mods |=
3146  (msg.wParam & MK_SHIFT) ? WUHOO_KMOD_SHIFT : WUHOO_KMOD_NONE;
3147 
3148  if (win32_window->window != GetCapture())
3149  SetCapture(win32_window->window);
3150  break;
3151  case WM_DEADCHAR:
3152  event->data.key.mods |= (int)WUHOO_KMOD_DEADCHAR;
3153  /* fallthrough */
3154  case WM_CHAR: {
3155  ch = (TCHAR)msg.wParam;
3156  WasDown = (msg.lParam >> 31) & 1;
3157  szBuf[0] = ch;
3158  szBuf[1] = 0;
3159 #ifdef WUHOO_UNICODE
3160  WuhooUTF8* data_ptr = &event->data.key.character[0];
3161  const WuhooUTF16* datap = (WuhooUTF16*)&szBuf[0];
3162  WuhooConversionResult res = WuhooConvertUTF16toUTF8(
3163  &datap, datap + sizeof(ch), &data_ptr,
3164  data_ptr + WUHOO_MAX_CHARACTER_SIZE,
3165  WuhooStrictConversion);
3166 #else
3167  event->data.key.character[0] = ch;
3168 #endif
3169 
3170  } break;
3171  case WM_KEYUP:
3172  event->type = WUHOO_EVT_KEY;
3173  event->data.key.code = WUHOO_VKEY_UNKNOWN;
3174  event->data.key.state = WUHOO_KSTATE_UP;
3175  event->data.key.character[0] = 0;
3176 
3177  switch (msg.wParam) {
3178  case VK_CONTROL:
3179  event->data.key.mods &= ~WUHOO_KMOD_CTRL;
3180  event->data.key.code = WUHOO_VKEY_CONTROL;
3181  break;
3182  case VK_SHIFT:
3183  event->data.key.mods &= ~WUHOO_KMOD_SHIFT;
3184  event->data.key.code = WUHOO_VKEY_SHIFT;
3185  break;
3186  case VK_MENU:
3187  event->data.key.mods &= ~WUHOO_KMOD_ALT;
3188  event->data.key.code = WUHOO_VKEY_ALT;
3189  break;
3190  case VK_LEFT: event->data.key.code = WUHOO_VKEY_LEFT; break;
3191  case VK_UP: event->data.key.code = WUHOO_VKEY_UP; break;
3192  case VK_DOWN: event->data.key.code = WUHOO_VKEY_DOWN; break;
3193  case VK_RIGHT: event->data.key.code = WUHOO_VKEY_RIGHT; break;
3194  case VK_ESCAPE: event->data.key.code = WUHOO_VKEY_ESCAPE; break;
3195  case VK_RETURN: event->data.key.code = WUHOO_VKEY_ENTER; break;
3196  default:
3197  if ((msg.wParam >= '0' && msg.wParam <= '9') ||
3198  (msg.wParam >= 0x41 && msg.wParam <= 0x5A)) {
3199  event->data.key.code = (WuhooKeyCode)msg.wParam;
3200  }
3201  break;
3202  }
3203  break;
3204  case WM_KEYDOWN:
3205  event->type = WUHOO_EVT_KEY;
3206  event->data.key.code = WUHOO_VKEY_UNKNOWN;
3207  event->data.key.state = WUHOO_KSTATE_DOWN;
3208  event->data.key.character[0] = 0;
3209 
3210  switch (msg.wParam) {
3211  case VK_CONTROL:
3212  event->data.key.mods |= (GetKeyState(VK_LCONTROL) & 0x8000)
3213  ? WUHOO_KMOD_LCTRL
3214  : WUHOO_KMOD_RCTRL;
3215  event->data.key.code = WUHOO_VKEY_CONTROL;
3216  break;
3217  case VK_SHIFT:
3218  event->data.key.mods |= (GetKeyState(VK_LSHIFT) & 0x8000)
3219  ? WUHOO_KMOD_LSHIFT
3220  : WUHOO_KMOD_RSHIFT;
3221  event->data.key.code = WUHOO_VKEY_SHIFT;
3222  break;
3223  case VK_MENU:
3224  event->data.key.mods |= (GetKeyState(VK_LMENU) & 0x8000)
3225  ? WUHOO_KMOD_LALT
3226  : WUHOO_KMOD_RALT;
3227  event->data.key.code = WUHOO_VKEY_ALT;
3228  break;
3229  case VK_LEFT: event->data.key.code = WUHOO_VKEY_LEFT; break;
3230  case VK_UP: event->data.key.code = WUHOO_VKEY_UP; break;
3231  case VK_DOWN: event->data.key.code = WUHOO_VKEY_DOWN; break;
3232  case VK_RIGHT: event->data.key.code = WUHOO_VKEY_RIGHT; break;
3233  case VK_ESCAPE: event->data.key.code = WUHOO_VKEY_ESCAPE; break;
3234  case VK_RETURN: event->data.key.code = WUHOO_VKEY_ENTER; break;
3235  default:
3236  if ((msg.wParam >= '0' && msg.wParam <= '9') ||
3237  (msg.wParam >= 0x41 && msg.wParam <= 0x5A)) {
3238  event->data.key.code = (WuhooKeyCode)msg.wParam;
3239  }
3240  break;
3241  }
3242  break;
3243  default: break;
3244  }
3245 
3246  TranslateMessage(&msg);
3247  if (msg.message == WM_KEYDOWN)
3248  continue;
3249  DispatchMessage(&msg);
3250  break;
3251  } else {
3252  break;
3253  }
3254  }
3255 
3256  if (WuhooNo == window->is_alive) {
3257  event->type = WUHOO_EVT_WINDOW;
3258  event->data.window.state = WUHOO_WSTATE_CLOSED;
3259 
3260  return WuhooSuccess;
3261  }
3262 
3263  if (window->window_flags & WUHOO_WINDOW_FLAG_CLOSED) {
3264  event->type = WUHOO_EVT_WINDOW;
3265  event->data.window.state = WUHOO_WSTATE_CLOSED;
3266  event->data.window.data1 = window->cwidth;
3267  event->data.window.data2 = window->cheight;
3268 
3269  return WuhooSuccess;
3270  } else if (window->window_flags & WUHOO_WINDOW_FLAG_RESIZED) {
3271  event->type = WUHOO_EVT_WINDOW;
3272  event->data.window.state = WUHOO_WSTATE_RESIZED;
3273  event->data.window.data1 = window->cwidth;
3274  event->data.window.data2 = window->cheight;
3275  window->window_flags &= ~WUHOO_WINDOW_FLAG_RESIZED;
3276 
3277  return WuhooSuccess;
3278  } else if (window->window_flags & WUHOO_WINDOW_FLAG_FOCUS_LOST) {
3279  event->type = WUHOO_EVT_WINDOW;
3280  event->data.window.state = WUHOO_WSTATE_UNFOCUSED;
3281  event->data.window.data1 = window->cwidth;
3282  event->data.window.data2 = window->cheight;
3283  window->window_flags &= ~WUHOO_WINDOW_FLAG_FOCUS_LOST;
3284 
3285  return WuhooSuccess;
3286  } else if (window->window_flags & WUHOO_WINDOW_FLAG_FOCUS_GAINED) {
3287  event->type = WUHOO_EVT_WINDOW;
3288  event->data.window.state = WUHOO_WSTATE_FOCUSED;
3289  event->data.window.data1 = window->cwidth;
3290  event->data.window.data2 = window->cheight;
3291  window->window_flags &= ~WUHOO_WINDOW_FLAG_FOCUS_GAINED;
3292 
3293  return WuhooSuccess;
3294  } else if (window->window_flags & WUHOO_WINDOW_FLAG_FULL_SCREEN) {
3295  event->type = WUHOO_EVT_WINDOW;
3296  event->data.window.state = WUHOO_WSTATE_FULL_SCREENED;
3297  event->data.window.data1 = window->cwidth;
3298  event->data.window.data2 = window->cheight;
3299  window->window_flags &= ~WUHOO_WINDOW_FLAG_FULL_SCREEN;
3300 
3301  return WuhooSuccess;
3302  } else if (window->window_flags & WUHOO_WINDOW_FLAG_MOVED) {
3303  event->type = WUHOO_EVT_WINDOW;
3304  event->data.window.state = WUHOO_WSTATE_MOVED;
3305  event->data.window.data1 = window->x;
3306  event->data.window.data2 = window->y;
3307  window->window_flags &= ~WUHOO_WINDOW_FLAG_MOVED;
3308 
3309  return WuhooSuccess;
3310  } else if (window->window_flags & WUHOO_WINDOW_FLAG_REGION_UPDATED) {
3311  event->type = WUHOO_EVT_WINDOW;
3312  event->data.window.state = WUHOO_WSTATE_INVALIDATED;
3313  event->data.window.data1 = window->cwidth;
3314  event->data.window.data2 = window->cheight;
3315  window->window_flags &= ~WUHOO_WINDOW_FLAG_REGION_UPDATED;
3316 
3317  return WuhooSuccess;
3318  }
3319 
3320  return WuhooSuccess;
3321 }
3322 
3323 #ifdef WUHOO_OPENGL_ENABLE
3324 WuhooInternal WuhooResult
3325 _WuhooOpenGLContextInit(WuhooWindow* window)
3326 {
3327  WuhooResult result = WuhooSuccess;
3328 
3329  WuhooWindowWin32* win32_window;
3330  win32_window = (WuhooWindowWin32*)window->platform_window;
3331  win32_window->dc = GetDC(win32_window->window);
3332 
3333  int gl_attrs[64];
3334  int gl_index = 0;
3335  gl_attrs[gl_index++] = WUHOO_WGL_PIXEL_TYPE_ARB;
3336  gl_attrs[gl_index++] = WUHOO_WGL_TYPE_RGBA_ARB;
3337  gl_attrs[gl_index++] = WUHOO_WGL_ACCELERATION_ARB;
3338  gl_attrs[gl_index++] = WUHOO_WGL_FULL_ACCELERATION_ARB;
3339  gl_attrs[gl_index++] = WUHOO_WGL_SUPPORT_OPENGL_ARB;
3340  gl_attrs[gl_index++] = 1;
3341  gl_attrs[gl_index++] = WUHOO_WGL_COLOR_BITS_ARB;
3342  gl_attrs[gl_index++] = window->gl_framebuffer.redBits +
3343  window->gl_framebuffer.greenBits +
3344  window->gl_framebuffer.blueBits;
3345  gl_attrs[gl_index++] = WUHOO_WGL_DEPTH_BITS_ARB;
3346  gl_attrs[gl_index++] = window->gl_framebuffer.depthBits;
3347  gl_attrs[gl_index++] = WUHOO_WGL_STENCIL_BITS_ARB;
3348  gl_attrs[gl_index++] = window->gl_framebuffer.stencilBits;
3349  gl_attrs[gl_index++] = WUHOO_WGL_RED_BITS_ARB;
3350  gl_attrs[gl_index++] = window->gl_framebuffer.redBits;
3351  gl_attrs[gl_index++] = WUHOO_WGL_GREEN_BITS_ARB;
3352  gl_attrs[gl_index++] = window->gl_framebuffer.greenBits;
3353  gl_attrs[gl_index++] = WUHOO_WGL_BLUE_BITS_ARB;
3354  gl_attrs[gl_index++] = window->gl_framebuffer.blueBits;
3355  gl_attrs[gl_index++] = WUHOO_WGL_ALPHA_BITS_ARB;
3356  gl_attrs[gl_index++] = window->gl_framebuffer.alphaBits;
3357  gl_attrs[gl_index++] = WUHOO_WGL_ACCUM_BITS_ARB;
3358  gl_attrs[gl_index++] = window->gl_framebuffer.accumRedBits +
3359  window->gl_framebuffer.accumGreenBits +
3360  window->gl_framebuffer.accumBlueBits;
3361  gl_attrs[gl_index++] = WUHOO_WGL_ACCUM_RED_BITS_ARB;
3362  gl_attrs[gl_index++] = window->gl_framebuffer.accumRedBits;
3363  gl_attrs[gl_index++] = WUHOO_WGL_ACCUM_GREEN_BITS_ARB;
3364  gl_attrs[gl_index++] = window->gl_framebuffer.accumGreenBits;
3365  gl_attrs[gl_index++] = WUHOO_WGL_ACCUM_BLUE_BITS_ARB;
3366  gl_attrs[gl_index++] = window->gl_framebuffer.accumBlueBits;
3367  gl_attrs[gl_index++] = WUHOO_WGL_ACCUM_ALPHA_BITS_ARB;
3368  gl_attrs[gl_index++] = window->gl_framebuffer.accumAlphaBits;
3369  gl_attrs[gl_index++] = WUHOO_WGL_DOUBLE_BUFFER_ARB;
3370  gl_attrs[gl_index++] =
3371  (WuhooTrue == window->gl_framebuffer.doublebuffer) ? 1 : 0;
3372  gl_attrs[gl_index++] = WUHOO_WGL_STEREO_ARB;
3373  gl_attrs[gl_index++] = (WuhooTrue == window->gl_framebuffer.stereo) ? 1 : 0;
3374  gl_attrs[gl_index++] = 0;
3375 
3376  int pixel_format;
3377  UINT num_formats;
3378  WUHOO_wglChoosePixelFormatARB(win32_window->dc, gl_attrs, 0, 1, &pixel_format,
3379  &num_formats);
3380  if (0 == num_formats) {
3381  return (WuhooResult)WUHOO_PLATFORM_API_STRING
3382  " : failed to find suitable pixel format.";
3383  }
3384 
3385  PIXELFORMATDESCRIPTOR pfd;
3386  DescribePixelFormat(win32_window->dc, pixel_format, sizeof(pfd), &pfd);
3387  if (!SetPixelFormat(win32_window->dc, pixel_format, &pfd)) {
3388  return (WuhooResult)WUHOO_PLATFORM_API_STRING " : SetPixelFormat failed.";
3389  }
3390 
3391  // Specify that we want to create an OpenGL 3.3 core profile context
3392  int gl33_attribs[] = {
3393  // WUHOO_WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
3394  // WUHOO_WGL_CONTEXT_MINOR_VERSION_ARB, 3,
3395  WUHOO_WGL_CONTEXT_PROFILE_MASK_ARB,
3396  WUHOO_WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
3397  0,
3398  };
3399 
3400  HGLRC gl33_context =
3401  WUHOO_wglCreateContextAttribsARB(win32_window->dc, 0, gl33_attribs);
3402  if (!gl33_context) {
3403  return (WuhooResult)WUHOO_PLATFORM_API_STRING
3404  " : Failed to create a modern GL context.";
3405  }
3406 
3407  if (!wglMakeCurrent(win32_window->dc, gl33_context)) {
3408  wglDeleteContext(win32_window->glrc);
3409  return (WuhooResult)WUHOO_PLATFORM_API_STRING
3410  " : wglMakeCurrent failed with a modern GL context.";
3411  }
3412 
3413  win32_window->glrc = gl33_context;
3414 
3415  return result;
3416 }
3417 #endif /* WUHOO_OPENGL_ENABLE */
3418 
3419 #ifdef WUHOO_OPENGL_ENABLE
3420 WuhooInternal WuhooResult
3421 _WuhooOpenGLExtensionsInit(WuhooWindow* window)
3422 {
3423  WuhooResult result = WuhooSuccess;
3424 
3425  WuhooWindowWin32* win32_window;
3426  win32_window = (WuhooWindowWin32*)window->platform_window;
3427 
3428  // Before we can load extensions, we need a dummy OpenGL context, created
3429  // using a dummy window. We use a dummy window because you can only set the
3430  // pixel format for a window once. For the real window, we want to use
3431  // wglChoosePixelFormatARB (so we can potentially specify options that aren't
3432  // available in PIXELFORMATDESCRIPTOR), but we can't load and use that before
3433  // we have a context.
3434  WNDCLASS window_class = { 0 };
3435  LPCTSTR lpszClassName = TEXT("Wuhoo_WGL");
3436  HINSTANCE hInstance = GetModuleHandle(0);
3437  window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
3438  window_class.cbClsExtra = 0;
3439  window_class.cbWndExtra = 0;
3440  window_class.lpfnWndProc = DefWindowProc;
3441  window_class.hInstance = hInstance;
3442  window_class.lpszClassName = lpszClassName;
3443  window_class.hIcon = LoadIcon(NULL, IDI_WINLOGO);
3444  window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
3445  window_class.hbrBackground = NULL;
3446  window_class.lpszMenuName = NULL;
3447 
3448  if (!RegisterClass(&window_class)) {
3449  return (WuhooResult)WUHOO_PLATFORM_API_STRING
3450  " : Failed to register dummy OpenGL window class for creating a modern "
3451  "GL context.";
3452  }
3453 
3454  HWND dummy_window =
3455  CreateWindowEx(0, window_class.lpszClassName, TEXT("Dummy OpenGL Window"),
3456  0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
3457  CW_USEDEFAULT, 0, 0, window_class.hInstance, 0);
3458 
3459  if (NULL == dummy_window) {
3460  UnregisterClass(lpszClassName, hInstance);
3461  return (WuhooResult)WUHOO_PLATFORM_API_STRING
3462  " : Failed to create dummy OpenGL window for creating a modern GL "
3463  "context.";
3464  }
3465 
3466  HDC dummy_dc = GetDC(dummy_window);
3467  PIXELFORMATDESCRIPTOR pfd = { 0 };
3468  pfd.nSize = sizeof(pfd);
3469  pfd.nVersion = 1;
3470  pfd.iPixelType = PFD_TYPE_RGBA;
3471  pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
3472  pfd.dwFlags |= (WuhooTrue == window->gl_framebuffer.doublebuffer)
3473  ? PFD_DOUBLEBUFFER
3474  : pfd.dwFlags;
3475  pfd.cColorBits = window->gl_framebuffer.redBits +
3476  window->gl_framebuffer.greenBits +
3477  window->gl_framebuffer.blueBits;
3478  pfd.cRedBits = window->gl_framebuffer.redBits;
3479  pfd.cGreenBits = window->gl_framebuffer.greenBits;
3480  pfd.cBlueBits = window->gl_framebuffer.blueBits;
3481  pfd.cAlphaBits = window->gl_framebuffer.alphaBits;
3482  pfd.cAccumBits = window->gl_framebuffer.accumRedBits +
3483  window->gl_framebuffer.accumGreenBits +
3484  window->gl_framebuffer.accumBlueBits;
3485  pfd.cAccumRedBits = window->gl_framebuffer.accumRedBits;
3486  pfd.cAccumGreenBits = window->gl_framebuffer.accumGreenBits;
3487  pfd.cAccumBlueBits = window->gl_framebuffer.accumBlueBits;
3488  pfd.cAccumAlphaBits = window->gl_framebuffer.accumAlphaBits;
3489  pfd.iLayerType = PFD_MAIN_PLANE;
3490  pfd.cDepthBits = window->gl_framebuffer.depthBits;
3491  pfd.cStencilBits = window->gl_framebuffer.stencilBits;
3492 
3493  int pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
3494  if (0 == pixel_format) {
3495  ReleaseDC(dummy_window, dummy_dc);
3496  DestroyWindow(dummy_window);
3497  UnregisterClass(lpszClassName, hInstance);
3498  return (WuhooResult)WUHOO_PLATFORM_API_STRING
3499  " : ChoosePixelFormat failed for dummy OpenGL window.";
3500  }
3501  if (!SetPixelFormat(dummy_dc, pixel_format, &pfd)) {
3502  ReleaseDC(dummy_window, dummy_dc);
3503  DestroyWindow(dummy_window);
3504  UnregisterClass(lpszClassName, hInstance);
3505  return (WuhooResult)WUHOO_PLATFORM_API_STRING
3506  " : SetPixelFormat failed for dummy OpenGL window.";
3507  }
3508 
3509  HGLRC dummy_context = wglCreateContext(dummy_dc);
3510  if (!dummy_context) {
3511  wglMakeCurrent(dummy_dc, 0);
3512  wglDeleteContext(dummy_context);
3513  ReleaseDC(dummy_window, dummy_dc);
3514  DestroyWindow(dummy_window);
3515  UnregisterClass(lpszClassName, hInstance);
3516  return (WuhooResult)WUHOO_PLATFORM_API_STRING
3517  " : wglCreateContext failed for dummy OpenGL window.";
3518  }
3519 
3520  if (!wglMakeCurrent(dummy_dc, dummy_context)) {
3521  wglMakeCurrent(dummy_dc, 0);
3522  wglDeleteContext(dummy_context);
3523  ReleaseDC(dummy_window, dummy_dc);
3524  DestroyWindow(dummy_window);
3525  UnregisterClass(lpszClassName, hInstance);
3526  return (WuhooResult)WUHOO_PLATFORM_API_STRING
3527  " : wglMakeCurrent failed for dummy OpenGL window.";
3528  }
3529 
3530  WUHOO_wglCreateContextAttribsARB =
3531  (wglCreateContextAttribsARB_type*)wglGetProcAddress(
3532  "wglCreateContextAttribsARB");
3533  WUHOO_wglChoosePixelFormatARB =
3534  (wglChoosePixelFormatARB_type*)wglGetProcAddress("wglChoosePixelFormatARB");
3535 
3536  wglMakeCurrent(dummy_dc, 0);
3537  wglDeleteContext(dummy_context);
3538  ReleaseDC(dummy_window, dummy_dc);
3539  DestroyWindow(dummy_window);
3540  UnregisterClass(lpszClassName, hInstance);
3541 
3542  return result;
3543 }
3544 #endif /* WUHOO_OPENGL_ENABLE */
3545 
3546 WuhooResult
3547 _WuhooWindowShowWin32(WuhooWindow* window)
3548 {
3549  WuhooWindowWin32* win32_window;
3550 
3551  if (window == WuhooNull) {
3552  return (WuhooResult) "Invalid window";
3553  }
3554 
3555  if (WuhooNo == window->is_initialized) {
3556  return (WuhooResult) "Invalid window initialization";
3557  }
3558 
3559  win32_window = (WuhooWindowWin32*)window->platform_window;
3560 
3561 #ifdef WUHOO_OPENGL_ENABLE
3562  if (WuhooOnlySet(window->flags, WUHOO_FLAG_OPENGL)) {
3563  _WuhooOpenGLExtensionsInit(window);
3564  _WuhooOpenGLContextInit(window);
3565  }
3566 #endif /* WUHOO_OPENGL_ENABLE */
3567 
3568  ShowWindow(win32_window->window, SW_SHOW);
3569  UpdateWindow(win32_window->window);
3570 
3571  return WuhooSuccess;
3572 }
3573 
3574 LRESULT CALLBACK
3575 WuhooWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
3576 #if _WIN32
3577 WuhooResult
3578 _WuhooWindowCreateWin32(WuhooWindow* window, int posx, int posy,
3579  WuhooSize width, WuhooSize height, const char* title,
3580  WuhooFlags flags)
3581 {
3582  WuhooWindowWin32* win32_window = (WuhooWindowWin32*)WuhooNull;
3583  WuhooResult result = WuhooSuccess;
3584 
3585  win32_window = (WuhooWindowWin32*)&window->memory[0];
3586 
3587  window->platform_window = win32_window;
3588  window->convert_rgba = &WuhooConvertRGBAtoBGRA;
3589 
3590  HINSTANCE hInstance = GetModuleHandle((TCHAR*)WuhooNull);
3591  win32_window->app = hInstance;
3592 
3593  HWND hWnd;
3594  TCHAR buffer[32] = TEXT("Wuhoo 1");
3595  WNDCLASS windowClass = { 0 };
3596  int class_index = 1;
3597  while (0 != GetClassInfo(hInstance, buffer, &windowClass) &&
3598  (class_index < 10)) {
3599  buffer[6] = class_index++ + '0';
3600  }
3601  if (class_index == 10) {
3602  return (WuhooResult)WUHOO_PLATFORM_API_STRING
3603  " : Trying to open too many windows";
3604  }
3605 
3606  windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
3607  windowClass.lpfnWndProc = WuhooWindowProc;
3608  windowClass.cbClsExtra = 0;
3609  windowClass.cbWndExtra = 0;
3610  windowClass.hInstance = hInstance; // Don't need hInstance
3611  windowClass.hIcon = LoadIcon(NULL, IDI_WINLOGO);
3612  windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
3613  windowClass.hbrBackground = NULL;
3614  windowClass.lpszMenuName = NULL;
3615  windowClass.lpszClassName = buffer;
3616  if (!RegisterClass(&windowClass)) {
3617  return (WuhooResult)WUHOO_PLATFORM_API_STRING " : RegisterClass failed";
3618  }
3619 
3620  DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
3621  DWORD dwStyle = (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU);
3622  dwStyle |= (WUHOO_FLAG_RESIZEABLE & flags)
3623  ? (WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME)
3624  : 0;
3625 
3626  if (WUHOO_FLAG_BORDERLESS & flags)
3627  dwStyle = WS_POPUP;
3628 
3629  RECT window_rect = { 0, 0, (LONG)width,
3630  (LONG)height }; // set the size, but not the position
3631  if (WUHOO_FLAG_CLIENT_REGION & flags)
3632  AdjustWindowRect(&window_rect, dwStyle, FALSE); // adjust the size
3633 
3634 #ifdef WUHOO_UNICODE
3635  WuhooUTF16 encoded_title[WUHOO_MAX_ENCODED_TITLE_LENGTH];
3636  WuhooUTF16* encoded_title_ptr = &encoded_title[0];
3637  WuhooZeroInit(encoded_title, sizeof(encoded_title));
3638  size_t title_length = WuhooStringLength(title, WUHOO_MAX_TITLE_LENGTH);
3639  WuhooConvertUTF8toUTF16((const WuhooUTF8 **)&title, (const WuhooUTF8 *)title + title_length, &encoded_title_ptr,
3640  encoded_title_ptr + WUHOO_MAX_ENCODED_TITLE_LENGTH,
3641  WuhooStrictConversion);
3642 #else
3643  const WuhooUTF8* encoded_title = (const WuhooUTF8*)title;
3644 #endif
3645 
3646  posx = (WuhooDefaultPosition == posx || WuhooDefaultPosition == posy)
3647  ? CW_USEDEFAULT
3648  : posx;
3649  hWnd = CreateWindowEx(dwExStyle, buffer, (WUHOO_FLAG_TITLED & flags) ? (TCHAR*)encoded_title : NULL, dwStyle, posx,
3650  posy, window_rect.right - window_rect.left,
3651  window_rect.bottom - window_rect.top, NULL, NULL,
3652  hInstance, window);
3653 
3654  if (NULL == hWnd) {
3655  return (WuhooResult)WUHOO_PLATFORM_API_STRING " : CreateWindowEx failed";
3656  }
3657 
3658  RECT crect, wrect;
3659  GetWindowRect(hWnd, &wrect);
3660  GetClientRect(hWnd, &crect);
3661 
3662  win32_window->window = hWnd;
3663  window->width = wrect.right - wrect.left;
3664  window->height = wrect.bottom - wrect.top;
3665  window->cwidth = crect.right - crect.left;
3666  window->cheight = crect.bottom - crect.top;
3667 
3668  if (WUHOO_FLAG_FILE_DROP & flags) {
3669  DragAcceptFiles(hWnd, TRUE);
3670  }
3671 
3672  return WuhooSuccess;
3673 }
3674 #endif
3675 
3676 LRESULT CALLBACK
3677 WuhooWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
3678 {
3679  WuhooWindow* window = (WuhooWindow*)GetProp(hWnd, TEXT("Wuhoo"));
3680  WuhooWindowWin32* win32_window =
3681  (NULL != window) ? (WuhooWindowWin32*)window->platform_window : NULL;
3682  HDC dc;
3683  LONG height;
3684  LONG width;
3685  RECT wrect;
3686 
3687  switch (message) {
3688  case WM_INPUTLANGCHANGEREQUEST: break;
3689  case WM_INPUTLANGCHANGE: break;
3690  case WM_DESTROY: break;
3691  case WM_QUIT: break;
3692  case WM_PAINT:
3693  window->window_flags |= WUHOO_WINDOW_FLAG_REGION_UPDATED;
3694  break;
3695  case WM_CLOSE:
3696  window->window_flags |= WUHOO_WINDOW_FLAG_CLOSED;
3697  window->window_flags |= WUHOO_WINDOW_FLAG_REGION_UPDATED;
3698  PostQuitMessage(0);
3699  break;
3700  case WM_EXITSIZEMOVE:
3701  window->window_flags |= WUHOO_WINDOW_FLAG_RESIZED;
3702  window->window_flags |= WUHOO_WINDOW_FLAG_REGION_UPDATED;
3703  break;
3704  case WM_SETFOCUS:
3705  window->window_flags |= WUHOO_WINDOW_FLAG_FOCUS_GAINED;
3706  break;
3707  case WM_KILLFOCUS:
3708  window->window_flags |= WUHOO_WINDOW_FLAG_FOCUS_LOST;
3709  break;
3710  case WM_MOVE:
3711  window->window_flags |= WUHOO_WINDOW_FLAG_MOVED;
3712  window->x = (int)(short)LOWORD(lParam); // horizontal position
3713  window->y = (int)(short)HIWORD(lParam); // vertical position
3714  break;
3715  case WM_SIZE:
3716  width = LOWORD(lParam);
3717  height = HIWORD(lParam);
3718 
3719  window->window_flags |= WUHOO_WINDOW_FLAG_RESIZED;
3720  window->window_flags |= WUHOO_WINDOW_FLAG_REGION_UPDATED;
3721  if (SIZE_MAXIMIZED == wParam) {
3722  window->window_flags |= WUHOO_WINDOW_FLAG_MAXIMIZED;
3723  } else if (SIZE_MINIMIZED == wParam) {
3724  window->window_flags |= WUHOO_WINDOW_FLAG_MINIMIZED;
3725  } else if (SIZE_RESTORED == wParam) {
3726  }
3727 
3728  GetWindowRect(win32_window->window, &wrect);
3729  window->width = wrect.right - wrect.left;
3730  window->height = wrect.bottom - wrect.top;
3731  window->cwidth = width;
3732  window->cheight = height;
3733 
3734  /* Maybe not needed because of OWNDC */
3735  dc = GetDC(hWnd);
3736  if (dc != win32_window->dc) {
3737  win32_window->dc = (HDC)dc;
3738  }
3739 
3740  if (!(WUHOO_FLAG_CANVAS & window->flags)) {
3741  break;
3742  }
3743 
3744  BITMAPINFO bmi;
3745 
3746  bmi.bmiHeader.biSize = sizeof(BITMAPINFO);
3747  bmi.bmiHeader.biWidth = width;
3748  bmi.bmiHeader.biHeight = -height; // Order pixels from top to bottom
3749  bmi.bmiHeader.biPlanes = 1;
3750  bmi.bmiHeader.biBitCount = 32; // last byte not used, 32 bit for alignment
3751  bmi.bmiHeader.biCompression = BI_RGB;
3752  bmi.bmiHeader.biSizeImage = 0;
3753  bmi.bmiHeader.biXPelsPerMeter = 0;
3754  bmi.bmiHeader.biYPelsPerMeter = 0;
3755  bmi.bmiHeader.biClrUsed = 0;
3756  bmi.bmiHeader.biClrImportant = 0;
3757  bmi.bmiColors[0].rgbBlue = 0;
3758  bmi.bmiColors[0].rgbGreen = 0;
3759  bmi.bmiColors[0].rgbRed = 0;
3760  bmi.bmiColors[0].rgbReserved = 0;
3761 
3762  if (WuhooNull != win32_window->mem_dc) {
3763  ReleaseDC(hWnd, win32_window->mem_dc);
3764  win32_window->mem_dc = (HDC)WuhooNull;
3765  }
3766 
3767  if (WuhooNull != win32_window->bitmap) {
3768  DeleteObject(win32_window->bitmap);
3769  win32_window->bitmap = (HBITMAP)WuhooNull;
3770  }
3771 
3772  dc = GetDC(hWnd);
3773  if (dc != win32_window->dc) {
3774  win32_window->dc = (HDC)dc;
3775  }
3776 
3777  win32_window->mem_dc = CreateCompatibleDC(dc);
3778  win32_window->dib = (WuhooRGBA*)WuhooNull;
3779  win32_window->bitmap =
3780  CreateDIBSection(win32_window->dc, &bmi, DIB_RGB_COLORS,
3781  (void**)&win32_window->dib, NULL, 0);
3782 
3783  break;
3784  case WM_CREATE:
3785  window = (WuhooWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams;
3786  win32_window = (WuhooWindowWin32*)window->platform_window;
3787 
3788  /* Possibly absolete */
3789  SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)window);
3790 
3791  SetProp(hWnd, TEXT("Wuhoo"), (HANDLE)window);
3792  break;
3793  default: return DefWindowProc(hWnd, message, wParam, lParam);
3794  }
3795 
3796  return DefWindowProc(hWnd, message, wParam, lParam);
3797 }
3798 #endif
3799 
3800 #ifdef __linux__
3801 
3802 /* For now, we only support X11 on linux systems */
3803 #define WUHOO_X11_ENABLE
3804 #ifdef WUHOO_X11_ENABLE
3805 #define WUHOO_X11_ENABLE
3806 #endif
3807 
3808 #define WuhooMaybeUnused __attribute__((unused))
3809 
3810 #endif
3811 
3812 #ifdef WUHOO_X11_ENABLE
3813 
3814 #include <X11/Xlib.h>
3815 #include <X11/Xutil.h>
3816 #include <X11/Xos.h>
3817 #include <X11/Xatom.h>
3818 #include <X11/keysym.h>
3819 
3820 #include <sys/ioctl.h>
3821 #include <sys/mman.h>
3822 #include <linux/fb.h>
3823 
3824 #include <locale.h>
3825 
3826 typedef struct
3827 {
3828  XSizeHints* size_hints;
3829  XWMHints* wm_hints;
3830  XClassHint* class_hints;
3831  XFontStruct* font_info;
3832  XImage* image;
3833  XIC ic;
3834  unsigned char* image_data;
3835  Visual* visual;
3836  Display* display;
3837 #ifdef WUHOO_OPENGL_ENABLE
3838  GLXContext glc;
3839 #endif
3840  int prev_x;
3841  int prev_y;
3842  int title_extents;
3843  int screen;
3844  int red_offset, red_length;
3845  int green_offset, green_length;
3846  int blue_offset, blue_length;
3847  int bytes_per_pixel;
3848  int depth;
3849 
3850  GC gc;
3851  Window window;
3852  Window root;
3853 
3854  Atom wmDeleteMessage;
3855  Atom wmState;
3856  Atom wmStateMaxHorz;
3857  Atom wmStateMaxVert;
3858  Atom wmStateFull;
3859  Atom wmStateHidden;
3860 
3861  Atom XA_text_uri_list;
3862  Atom XA_text_uri;
3863  Atom XA_text_plain;
3864  Atom XA_text;
3865  Atom XA_XdndSelection;
3866  Atom XA_XdndAware;
3867  Atom XA_XdndEnter;
3868  Atom XA_XdndLeave;
3869  Atom XA_XdndTypeList;
3870  Atom XA_XdndPosition;
3871  Atom XA_XdndActionCopy;
3872  Atom XA_XdndStatus;
3873  Atom XA_XdndDrop;
3874  Atom XA_XdndFinished;
3875  Atom XA_XdndReq;
3876  int XdndVersion;
3877 
3878 } WuhooWindowX11;
3879 
3880 void*
3881 WuhooMalloc(size_t count)
3882 {
3883  return mmap((void*)WuhooNull, // Map from the start of the 2^20th page
3884  count, // for one page length
3885  PROT_READ | PROT_WRITE,
3886  MAP_ANON | MAP_PRIVATE, // to a private block of hardware memory
3887  0, 0);
3888 }
3889 
3890 WuhooResult
3891 WuhooFree(void* mem)
3892 {
3893  munmap(mem, sizeof(WuhooWindow));
3894 
3895  return WuhooSuccess;
3896 }
3897 
3898 WuhooResult
3899 _WuhooWindowRegionGetX11(WuhooWindow* window, int* posx, int* posy,
3900  WuhooSize* width, WuhooSize* height)
3901 {
3902  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
3903  WuhooResult result = WuhooSuccess;
3904 
3905  int window_posx, window_posy;
3906  Window child;
3907  XWindowAttributes xwa;
3908  XTranslateCoordinates(x11_window->display, x11_window->window,
3909  x11_window->root, 0, 0, &window_posx, &window_posy,
3910  &child);
3911  XGetWindowAttributes(x11_window->display, x11_window->window, &xwa);
3912  if (WuhooNull != posx)
3913  *posx = window_posx - xwa.x;
3914  if (WuhooNull != posy)
3915  *posy = window_posy - xwa.y;
3916 
3917  if (WuhooNull != width)
3918  *width = xwa.width;
3919  if (WuhooNull != height)
3920  *height = xwa.height + x11_window->title_extents;
3921 
3922  return result;
3923 }
3924 
3925 WuhooResult
3926 _WuhooWindowClientRegionGetX11(WuhooWindow* window, int* posx, int* posy,
3927  WuhooSize* width, WuhooSize* height)
3928 {
3929  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
3930  WuhooResult result = WuhooSuccess;
3931 
3932  int window_posx, window_posy;
3933  Window child;
3934  XWindowAttributes xwa;
3935  XTranslateCoordinates(x11_window->display, x11_window->window,
3936  x11_window->root, 0, 0, &window_posx, &window_posy,
3937  &child);
3938  XGetWindowAttributes(x11_window->display, x11_window->window, &xwa);
3939  *posx = window_posx - xwa.x;
3940  *posy = window_posy - xwa.y;
3941 
3942  *width = xwa.width;
3943  *height = xwa.height;
3944 
3945  return result;
3946 }
3947 
3948 WuhooResult
3949 _WuhooWindowRegionSetX11(WuhooWindow* window, int posx, int posy,
3950  WuhooSize width, WuhooSize height)
3951 {
3952  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
3953  WuhooResult result = WuhooSuccess;
3954 
3955  height -= x11_window->title_extents;
3956 
3957  XMoveResizeWindow(x11_window->display, x11_window->window, posx, posy, width,
3958  height);
3959 
3960  window->window_flags |= WUHOO_WINDOW_FLAG_RESIZED;
3961 
3962  return result;
3963 }
3964 
3965 WuhooResult
3966 _WuhooWindowClientRegionSetX11(WuhooWindow* window, int posx, int posy,
3967  WuhooSize width, WuhooSize height)
3968 {
3969  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
3970  WuhooResult result = WuhooSuccess;
3971 
3972  XMoveResizeWindow(x11_window->display, x11_window->window, posx, posy, width,
3973  height);
3974 
3975  window->window_flags |= WUHOO_WINDOW_FLAG_RESIZED;
3976 
3977  return result;
3978 }
3979 
3980 WuhooInternal int wuhoo_TrappedErrorCode = 0;
3981 WuhooInternal int (*wuhoo_OldErrorHandle)(Display*, XErrorEvent*);
3982 
3983 WuhooInternal int
3984 _WuhooErrorHandleX11(Display* display, XErrorEvent* error)
3985 {
3986  wuhoo_TrappedErrorCode = error->error_code;
3987  return 0;
3988 }
3989 
3990 WuhooInternal void
3991 _WuhooTrapErrorsX11(void)
3992 {
3993  wuhoo_TrappedErrorCode = 0;
3994  wuhoo_OldErrorHandle = XSetErrorHandler(_WuhooErrorHandleX11);
3995 }
3996 
3997 WuhooInternal int
3998 _WuhooUntrapErrorsX11(void)
3999 {
4000  XSetErrorHandler(wuhoo_OldErrorHandle);
4001  return wuhoo_TrappedErrorCode;
4002 }
4003 
4004 WuhooResult
4005 _WuhooWindowBlitX11(WuhooWindow* window, WuhooRGBA* pixels, WuhooSize x,
4006  WuhooSize y, WuhooSize width, WuhooSize height)
4007 {
4008  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
4009  WuhooResult result = WuhooSuccess;
4010 
4011 #ifdef WUHOO_OPENGL_ENABLE
4012  if (WUHOO_FLAG_OPENGL & window->flags) {
4013  glXSwapBuffers(x11_window->display, x11_window->window);
4014 
4015  return WuhooSuccess;
4016  }
4017 #endif
4018 
4019  if (WUHOO_WINDOW_FLAG_CLOSED & window->window_flags ||
4020  WUHOO_WINDOW_FLAG_RESIZED & window->window_flags ||
4021  WUHOO_WINDOW_FLAG_REGION_UPDATED & window->window_flags) {
4022  /* If important window states have not been handled yet
4023  * don not proceed with blitting because this might
4024  * cause inconcistent behaviors. **REVISIT**
4025  */
4026  return WuhooSuccess;
4027  }
4028 
4029  Window root;
4030  unsigned int screen_depth;
4031  int screen_x;
4032  int screen_y;
4033  unsigned int screen_width;
4034  unsigned int screen_height;
4035  unsigned int screen_border_width;
4036 
4037  if (XGetGeometry(x11_window->display, x11_window->root, &root, &screen_x,
4038  &screen_y, &screen_width, &screen_height,
4039  &screen_border_width, &screen_depth) == False) {
4040  }
4041  int ax, ay;
4042  Window child;
4043  XWindowAttributes xwa;
4044  XTranslateCoordinates(x11_window->display, x11_window->window,
4045  x11_window->root, 0, 0, &ax, &ay, &child);
4046  XGetWindowAttributes(x11_window->display, x11_window->window, &xwa);
4047  window->x = ax - xwa.x;
4048  window->y = ay - xwa.y;
4049 
4050  window->cwidth = xwa.width;
4051  window->cheight = xwa.height;
4052 
4053  window->width = window->cwidth;
4054  window->height = window->cheight + x11_window->title_extents;
4055 
4056  int valid_width = screen_width - window->x;
4057  int valid_height = screen_height - x11_window->title_extents - window->y;
4058 
4059  valid_width = WuhooMini(valid_width, width);
4060  valid_height = WuhooMini(valid_height, height);
4061 
4062  /* https://stackoverflow.com/questions/43442675/how-to-use-xshmgetimage-and-xshmputimage
4063  */
4064  XImage* image = WuhooNull;
4065  _WuhooTrapErrorsX11();
4066  image = XGetImage(x11_window->display, x11_window->window, x, y, valid_width,
4067  valid_height, AllPlanes, ZPixmap);
4068  int trapped_error = _WuhooUntrapErrorsX11();
4069 
4070  if (trapped_error == BadMatch) {
4071  return (WuhooResult) "Invalid Region";
4072  }
4073 
4074  if (WuhooNull == image) {
4075  return (WuhooResult) "Failed to get a valid draw region";
4076  }
4077 
4078  window->convert_rgba(image->data, pixels, x, y, valid_width, valid_height,
4079  width, height);
4080 
4081  XPutImage(x11_window->display, x11_window->window, x11_window->gc, image, 0,
4082  0, x, y, valid_width, valid_height);
4083 
4084  XDestroyImage(image);
4085 
4086  return result;
4087 }
4088 
4089 WuhooResult
4090 _WuhooWindowDestroyX11(WuhooWindow* window)
4091 {
4092  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
4093  WuhooResult result = WuhooSuccess;
4094 
4095  if (None != x11_window->gc) {
4096  XFreeGC(x11_window->display, x11_window->gc);
4097  x11_window->gc = None;
4098  }
4099 
4100  if (None != x11_window->window) {
4101  XUnmapWindow(x11_window->display, x11_window->window);
4102  XDestroyWindow(x11_window->display, x11_window->window);
4103  x11_window->window = None;
4104  }
4105  if (None != x11_window->display) {
4106  XCloseDisplay(x11_window->display);
4107  x11_window->display = None;
4108  }
4109 
4110  return result;
4111 }
4112 
4113 WuhooResult
4114 _WuhooWindowSetTitleX11(WuhooWindow* window, const char* title)
4115 {
4116  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
4117  WuhooResult result = WuhooSuccess;
4118  XChangeProperty( x11_window->display, x11_window->window,
4119  XInternAtom(x11_window->display, "_NET_WM_NAME", False),
4120  XInternAtom(x11_window->display, "UTF8_STRING", False),
4121  8, PropModeReplace, (unsigned char *) title,
4122  WuhooStringLength(title, WUHOO_MAX_TITLE_LENGTH));
4123 
4124  return result;
4125 }
4126 
4127 WuhooInternal WuhooResult
4128 _WuhooWindowExtentsX11(WuhooWindow* window)
4129 {
4130  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
4131  WuhooResult result = WuhooSuccess;
4132 
4133  Display* d;
4134  Window w;
4135  int s;
4136 
4137  d = XOpenDisplay(NULL);
4138  if (d == NULL) {
4139  return WuhooSuccess;
4140  }
4141 
4142  s = DefaultScreen(d);
4143  w = XCreateSimpleWindow(d, RootWindow(d, s), 0, 0, 1, 1, 1, BlackPixel(d, s),
4144  WhitePixel(d, s));
4145  XSelectInput(d, w, ExposureMask | KeyPressMask);
4146  XMapWindow(d, w);
4147 
4148  /* Extended Window Manager Hints */
4149  /* https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472552416
4150  */
4151 
4152  Atom a, t;
4153 
4154  int f;
4155  unsigned char* data = 0;
4156  long* extents;
4157  XEvent e;
4158  unsigned long n, b;
4159 
4160  a = XInternAtom(d, "_NET_FRAME_EXTENTS", True); /* Property to check */
4161 
4162  /* Window manager doesn't set up the extents immediately */
4163  /* Wait until they are set up and there are 4 of them */
4164  while (XGetWindowProperty(d, w, a, 0, 4, False, AnyPropertyType, &t, &f, &n,
4165  &b, &data) != Success ||
4166  n != 4 || b != 0) {
4167  XNextEvent(d, &e);
4168  }
4169 
4170  /* OK got extents */
4171  if(WuhooNull != data) {
4172  extents = (long*)data;
4173  x11_window->title_extents = extents[2];
4174  XFree(data);
4175  }
4176  XCloseDisplay(d);
4177 
4178  return result;
4179 }
4180 
4181 WuhooResult
4182 _WuhooWindowCreateX11(WuhooWindow* window, int posx, int posy, WuhooSize width,
4183  WuhooSize height, const char* title, WuhooFlags flags)
4184 {
4185  WuhooWindowX11* x11_window = (WuhooWindowX11*)WuhooNull;
4186  WuhooResult result = WuhooSuccess;
4187  Colormap cmap;
4188  XSetWindowAttributes swa;
4189  XWindowAttributes gwa;
4190  int depth;
4191 
4192  x11_window = (WuhooWindowX11*)&window->memory[0];
4193  window->platform_window = x11_window;
4194 
4195  _WuhooWindowExtentsX11(window);
4196 
4197  /* fallback to LC_CTYPE in env */
4198  setlocale(LC_CTYPE, "");
4199  /* implementation-dependent behavior, on my machine it defaults to
4200  * XMODIFIERS in env */
4201 
4202  if (XSetLocaleModifiers("") == NULL) {
4203  return (WuhooResult)WUHOO_PLATFORM_API_STRING
4204  " : XSetLocaleModifiers failed";
4205  }
4206 
4207  x11_window->display = XOpenDisplay(WuhooNull);
4208 
4209  if (WuhooNull == x11_window->display) {
4210  return (WuhooResult)WUHOO_PLATFORM_API_STRING
4211  " : XOpenDisplay failed";
4212  }
4213 
4214  x11_window->root = DefaultRootWindow(x11_window->display);
4215  if (0 == x11_window->root) {
4216  return (WuhooResult)WUHOO_PLATFORM_API_STRING
4217  " : DefaultRootWindow failed";
4218  }
4219 
4220  /* By default, assume 24-bit RGB */
4221  x11_window->red_offset = 0;
4222  x11_window->red_length = 8;
4223 
4224  x11_window->green_offset = 8;
4225  x11_window->green_length = 8;
4226 
4227  x11_window->blue_offset = 16;
4228  x11_window->blue_length = 8;
4229 
4230  x11_window->bytes_per_pixel = 24 / 8;
4231 
4232  int fbfd = open("/dev/fb0", O_RDWR);
4233  if (fbfd >= 0) {
4234  struct fb_var_screeninfo vinfo;
4235  ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo);
4236  x11_window->red_offset = vinfo.red.offset;
4237  x11_window->red_length = vinfo.red.length;
4238 
4239  x11_window->green_offset = vinfo.green.offset;
4240  x11_window->green_length = vinfo.green.length;
4241 
4242  x11_window->blue_offset = vinfo.blue.offset;
4243  x11_window->blue_length = vinfo.blue.length;
4244 
4245  x11_window->bytes_per_pixel = vinfo.bits_per_pixel / 8;
4246  x11_window->depth = vinfo.bits_per_pixel;
4247 
4248  close(fbfd);
4249  }
4250 
4251 #ifdef WUHOO_OPENGL_ENABLE
4252  window->gl_framebuffer.depthBits;
4253  window->gl_framebuffer.redBits = x11_window->red_length;
4254  window->gl_framebuffer.greenBits = x11_window->green_length;
4255  window->gl_framebuffer.blueBits = x11_window->blue_length;
4256  window->gl_framebuffer.accumRedBits = x11_window->red_length;
4257  window->gl_framebuffer.accumGreenBits = x11_window->green_length;
4258  window->gl_framebuffer.accumBlueBits = x11_window->blue_length;
4259 
4260  int gl_index = 0;
4261 
4262  GLint gl_attrs[32];
4263  gl_attrs[gl_index++] = GLX_RGBA;
4264  gl_attrs[gl_index++] = GLX_DEPTH_SIZE;
4265  gl_attrs[gl_index++] = window->gl_framebuffer.depthBits;
4266  gl_attrs[gl_index++] = GLX_STENCIL_SIZE;
4267  gl_attrs[gl_index++] = window->gl_framebuffer.stencilBits;
4268  gl_attrs[gl_index++] = GLX_RED_SIZE;
4269  gl_attrs[gl_index++] = window->gl_framebuffer.redBits;
4270  gl_attrs[gl_index++] = GLX_GREEN_SIZE;
4271  gl_attrs[gl_index++] = window->gl_framebuffer.greenBits;
4272  gl_attrs[gl_index++] = GLX_BLUE_SIZE;
4273  gl_attrs[gl_index++] = window->gl_framebuffer.blueBits;
4274  gl_attrs[gl_index++] = GLX_ALPHA_SIZE;
4275  gl_attrs[gl_index++] = window->gl_framebuffer.alphaBits;
4276  gl_attrs[gl_index++] = GLX_ACCUM_RED_SIZE;
4277  gl_attrs[gl_index++] = window->gl_framebuffer.accumRedBits;
4278  gl_attrs[gl_index++] = GLX_ACCUM_GREEN_SIZE;
4279  gl_attrs[gl_index++] = window->gl_framebuffer.accumGreenBits;
4280  gl_attrs[gl_index++] = GLX_ACCUM_BLUE_SIZE;
4281  gl_attrs[gl_index++] = window->gl_framebuffer.accumBlueBits;
4282  gl_attrs[gl_index++] = GLX_ACCUM_ALPHA_SIZE;
4283  gl_attrs[gl_index++] = window->gl_framebuffer.accumAlphaBits;
4284  if (WuhooTrue == window->gl_framebuffer.doublebuffer)
4285  gl_attrs[gl_index++] = GLX_DOUBLEBUFFER;
4286  if (WuhooTrue == window->gl_framebuffer.stereo)
4287  gl_attrs[gl_index++] = GLX_STEREO;
4288  gl_attrs[gl_index++] = None;
4289 #endif
4290 
4291  int bytes_per_pixel = x11_window->bytes_per_pixel;
4292 
4293  int screen_num = DefaultScreen(x11_window->display);
4294  x11_window->screen = screen_num;
4295 
4296 #ifdef WUHOO_OPENGL_ENABLE
4297  XVisualInfo* vi;
4298 
4299  vi = glXChooseVisual(x11_window->display, 0, gl_attrs);
4300  cmap = XCreateColormap(x11_window->display, x11_window->root, vi->visual,
4301  AllocNone);
4302  depth = vi->depth;
4303  x11_window->visual = vi->visual;
4304 #else
4305  x11_window->visual = DefaultVisual(x11_window->display, x11_window->screen);
4306  cmap = XCreateColormap(x11_window->display, x11_window->root,
4307  x11_window->visual, AllocNone);
4308  depth = DefaultDepth(x11_window->display, x11_window->screen);
4309 #endif
4310 
4311  if (WuhooNull == x11_window->visual) {
4312  return (WuhooResult)WUHOO_PLATFORM_API_STRING
4313  " : Failed to create appropriate visual";
4314  }
4315 
4316  if (x11_window->red_offset == 11 && x11_window->green_offset == 5 &&
4317  x11_window->blue_offset == 0) {
4318  window->convert_rgba = &WuhooConvertRGBAtoR5G6B5;
4319  } else if (x11_window->red_offset == 16 && x11_window->green_offset == 8 &&
4320  x11_window->blue_offset == 0) {
4321  window->convert_rgba = &WuhooConvertRGBAtoBGRA;
4322  } else {
4323  window->convert_rgba = &WuhooConvertRGBAtoRGBA;
4324  }
4325 
4326  XSetWindowAttributes setwinattr;
4327  setwinattr.override_redirect = False;
4328  setwinattr.border_pixel = 0;
4329  setwinattr.colormap = cmap;
4330  setwinattr.event_mask = StructureNotifyMask | FocusChangeMask | ExposureMask |
4331  KeyPressMask | KeyReleaseMask | ButtonPressMask |
4332  ButtonReleaseMask | PointerMotionMask;
4333 
4334  /* Get geometry information about root window */
4335  Window root;
4336  int screen_x, screen_y;
4337  unsigned int screen_width, screen_height, border_width, root_depth;
4338  if (XGetGeometry(x11_window->display, x11_window->root, &root, &screen_x,
4339  &screen_y, &screen_width, &screen_height, &border_width,
4340  &root_depth) == False) {
4341  }
4342 
4343  window->x =
4344  (WuhooDefaultPosition != posx) ? posx : ((screen_width / 2) - (width / 2));
4345  window->y = (WuhooDefaultPosition != posy)
4346  ? posy
4347  : ((screen_height / 2) - (height / 2));
4348 
4349  window->width = width;
4350  window->height = height;
4351  window->height +=
4352  (WUHOO_FLAG_CLIENT_REGION & flags) ? x11_window->title_extents : 0;
4353 
4354  window->cwidth = window->width;
4355  window->cheight =
4356  window->height -
4357  ((WUHOO_FLAG_CLIENT_REGION & flags) ? x11_window->title_extents : 0);
4358  x11_window->window =
4359  XCreateWindow(x11_window->display, x11_window->root, window->x, window->y,
4360  window->cwidth, window->cheight, 0, depth, InputOutput,
4361  x11_window->visual, CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, &setwinattr);
4362 
4363  if (0 == x11_window->window) {
4364  return (WuhooResult)WUHOO_PLATFORM_API_STRING
4365  " : XCreateWindow failed";
4366  }
4367 
4368 #ifdef WUHOO_OPENGL_ENABLE
4369  x11_window->glc = glXCreateContext(x11_window->display, vi, NULL, GL_TRUE);
4370  if (WuhooNull == x11_window->glc) {
4371  return (WuhooResult)WUHOO_PLATFORM_API_STRING
4372  " : glXCreateContext failed";
4373  }
4374  glXMakeCurrent(x11_window->display, x11_window->window, x11_window->glc);
4375 #endif
4376 
4377  if (WUHOO_FLAG_TITLED & flags)
4378  XSetStandardProperties(x11_window->display, x11_window->window, title, title,
4379  None, NULL, 0, NULL);
4380 
4381  XSelectInput(x11_window->display, x11_window->window,
4382  StructureNotifyMask | FocusChangeMask | ExposureMask |
4383  KeyPressMask | KeyReleaseMask | ButtonPressMask |
4384  ButtonReleaseMask | PointerMotionMask);
4385  XSetWindowBorder(x11_window->display, x11_window->window, 1);
4386 
4387  x11_window->gc = XCreateGC(x11_window->display, x11_window->window, 0, NULL);
4388 
4389  x11_window->wmDeleteMessage =
4390  XInternAtom(x11_window->display, "WM_DELETE_WINDOW", 0);
4391  x11_window->wmState = XInternAtom(x11_window->display, "_NET_WM_STATE", True);
4392  x11_window->wmStateMaxHorz =
4393  XInternAtom(x11_window->display, "_NET_WM_STATE_MAXIMIZED_HORZ", True);
4394  x11_window->wmStateMaxVert =
4395  XInternAtom(x11_window->display, "_NET_WM_STATE_MAXIMIZED_VERT", True);
4396  x11_window->wmStateHidden =
4397  XInternAtom(x11_window->display, "_NET_WM_STATE_HIDDEN", True);
4398  x11_window->wmStateFull =
4399  XInternAtom(x11_window->display, "_NET_WM_STATE_FULLSCREEN", True);
4400 
4401  XSetWMProtocols(x11_window->display, x11_window->window,
4402  &x11_window->wmDeleteMessage, 1);
4403 
4404  if (WUHOO_FLAG_BORDERLESS & window->flags) {
4405  Atom wmMotifHints = XInternAtom(x11_window->display, "_MOTIF_WM_HINTS", True);
4406  if (None != wmMotifHints) {
4407  /* Hints used by Motif compliant window managers */
4408  struct
4409  {
4410  unsigned long flags;
4411  unsigned long functions;
4412  unsigned long decorations;
4413  long input_mode;
4414  unsigned long status;
4415  } MWMHints = {
4416  (1L << 1), 0, 0, 0, 0
4417  };
4418 
4419  XChangeProperty(x11_window->display, x11_window->window, wmMotifHints, wmMotifHints, 32, PropModeReplace, (unsigned char *) &MWMHints, sizeof(MWMHints) / sizeof(long));
4420  } else {
4421  /* set the transient hints instead, if necessary */
4422  XSetTransientForHint(x11_window->display, x11_window->window, RootWindow(x11_window->display, x11_window->screen));
4423  }
4424  }
4425 
4426  if (!(WUHOO_FLAG_RESIZEABLE & window->flags)) {
4427  XSizeHints* size_hints = XAllocSizeHints();
4428  size_hints->flags = PMinSize | PMaxSize;
4429  size_hints->min_width = size_hints->max_width = window->cwidth;
4430  size_hints->min_height = size_hints->max_height = window->cheight;
4431  XSetNormalHints(x11_window->display, x11_window->window, size_hints);
4432  XFree(size_hints);
4433  }
4434 
4435  /* Set up useful Atoms */
4436  if (WUHOO_FLAG_FILE_DROP & flags) {
4437  x11_window->XA_text_uri_list =
4438  XInternAtom(x11_window->display, "text/uri-list", False);
4439  x11_window->XA_XdndSelection =
4440  XInternAtom(x11_window->display, "XdndSelection", False);
4441  x11_window->XA_XdndAware =
4442  XInternAtom(x11_window->display, "XdndAware", False);
4443  x11_window->XA_XdndEnter =
4444  XInternAtom(x11_window->display, "XdndEnter", False);
4445  x11_window->XA_XdndLeave =
4446  XInternAtom(x11_window->display, "XdndLeave", False);
4447  x11_window->XA_XdndTypeList =
4448  XInternAtom(x11_window->display, "XdndTypeList", False);
4449  x11_window->XA_XdndPosition =
4450  XInternAtom(x11_window->display, "XdndPosition", False);
4451  x11_window->XA_XdndActionCopy =
4452  XInternAtom(x11_window->display, "XdndActionCopy", False);
4453  x11_window->XA_XdndStatus =
4454  XInternAtom(x11_window->display, "XdndStatus", False);
4455  x11_window->XA_XdndDrop =
4456  XInternAtom(x11_window->display, "XdndDrop", False);
4457  x11_window->XA_XdndFinished =
4458  XInternAtom(x11_window->display, "XdndFinished", False);
4459  x11_window->XA_XdndReq = None;
4460 
4461  x11_window->XdndVersion = 4;
4462  XChangeProperty(x11_window->display, x11_window->window,
4463  x11_window->XA_XdndAware, XA_ATOM, 32, PropModeReplace,
4464  (unsigned char*)&x11_window->XdndVersion, 1);
4465  }
4466 
4467  return result;
4468 }
4469 
4470 WuhooResult
4471 _WuhooWindowShowX11(WuhooWindow* window)
4472 {
4473  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
4474 
4475  XMapWindow(x11_window->display, x11_window->window);
4476  XMapRaised(x11_window->display, x11_window->window);
4477 
4478  XSetLocaleModifiers("");
4479 
4480  XIM xim = XOpenIM(x11_window->display, NULL, NULL, NULL);
4481  if (!xim) {
4482  // fallback to internal input method
4483  XSetLocaleModifiers("@im=none");
4484  xim = XOpenIM(x11_window->display, 0, 0, 0);
4485  }
4486 
4487  // X input context, you can have multiple for text boxes etc, but having a
4488  x11_window->ic = XCreateIC(
4489  xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow,
4490  x11_window->window, XNFocusWindow, x11_window->window, NULL);
4491 
4492  XSetICFocus(x11_window->ic);
4493 
4494  XMoveWindow(x11_window->display, x11_window->window, window->x, window->y);
4495 
4496  return WuhooSuccess;
4497 }
4498 
4499 WuhooInternal WuhooResult
4500 _WuhooWindowStateX11(WuhooWindow* window)
4501 {
4502  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
4503  WuhooResult result = WuhooSuccess;
4504 
4505  Atom type;
4506  int format;
4507  unsigned long nItem, bytesAfter;
4508  unsigned char* properties = NULL;
4509  while (XGetWindowProperty(x11_window->display, x11_window->window,
4510  x11_window->wmState, 0, (~0L) /* LONG_MAX */, False,
4511  AnyPropertyType, &type, &format, &nItem,
4512  &bytesAfter, &properties) != Success ||
4513  bytesAfter != 0) {
4514  }
4515 
4516  WuhooBoolean max_vertical_resize = WuhooFalse;
4517  WuhooBoolean max_horizontal_resize = WuhooFalse;
4518 
4519  int iItem;
4520  for (iItem = 0; iItem < nItem; ++iItem) {
4521  max_horizontal_resize = (((unsigned long *)properties)[iItem] == x11_window->wmStateMaxHorz) ? WuhooTrue : max_horizontal_resize;
4522  max_vertical_resize = (((unsigned long *)properties)[iItem] == x11_window->wmStateMaxVert) ? WuhooTrue : max_vertical_resize;
4523  }
4524 
4525  if ( WuhooTrue == max_horizontal_resize && WuhooTrue == max_vertical_resize ) {
4526  window->window_flags |= WUHOO_WINDOW_FLAG_FULL_SCREEN | WUHOO_WINDOW_FLAG_MAXIMIZED;
4527  }
4528 
4529  return result;
4530 }
4531 
4532 typedef struct
4533 {
4534  unsigned char* data;
4535  int format, count;
4536  Atom type;
4537 } WuhooPropertyX11;
4538 
4539 /* Reads property
4540  Must call X11_XFree on results
4541  */
4542 WuhooInternal void
4543 _WuhooReadPropertyX11(WuhooPropertyX11* p, Display* disp, Window w, Atom prop)
4544 {
4545  unsigned char* ret = NULL;
4546  Atom type;
4547  int fmt;
4548  unsigned long count;
4549  unsigned long bytes_left;
4550  int bytes_fetch = 0;
4551 
4552  do {
4553  if (ret != 0)
4554  XFree(ret);
4555  XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType,
4556  &type, &fmt, &count, &bytes_left, &ret);
4557  bytes_fetch += bytes_left;
4558  } while (bytes_left != 0);
4559 
4560  p->data = ret;
4561  p->format = fmt;
4562  p->count = count;
4563  p->type = type;
4564 }
4565 
4566 WuhooInternal WuhooKeyCode
4567 _WuhooKeyTranslateX11(KeySym ksym)
4568 {
4569  switch (ksym) {
4570  /* Undocumented KyeCode for X11X */
4571  case XK_Menu: return WUHOO_VKEY_MENU; break;
4572  case XK_comma: return WUHOO_VKEY_COMMA; break;
4573  case XK_semicolon: return WUHOO_VKEY_SEMICOLON; break;
4574  case XK_backslash: return WUHOO_VKEY_BACK_SLASH; break;
4575  case XK_slash: return WUHOO_VKEY_FORWARD_SLASH; break;
4576  case XK_quoteright: return WUHOO_VKEY_QUOTE; break;
4577  case XK_bracketleft: return WUHOO_VKEY_LEFT_BRACKET; break;
4578  case XK_bracketright: return WUHOO_VKEY_RIGHT_BRACKET; break;
4579  case XK_equal: return WUHOO_VKEY_EQUALS; break;
4580  case XK_minus: return WUHOO_VKEY_MINUS; break;
4581  case XK_period: return WUHOO_VKEY_PERIOD; break;
4582  case XK_grave: return WUHOO_VKEY_GRAVE; break;
4583  case XK_KP_Decimal: return WUHOO_VKEY_KPAD_DECIMAL; break;
4584  case XK_KP_Multiply: return WUHOO_VKEY_KPAD_MULITPLY; break;
4585  case XK_KP_Add: return WUHOO_VKEY_KPAD_PLUS; break;
4586  case XK_Num_Lock: return WUHOO_VKEY_KPAD_NUM_LOCK; break;
4587  case XK_KP_Divide: return WUHOO_VKEY_KPAD_DIVIDE; break;
4588  case XK_KP_Enter: return WUHOO_VKEY_KPAD_ENTER; break;
4589  case XK_KP_Subtract: return WUHOO_VKEY_KPAD_MINUS; break;
4590  case XK_KP_0: return WUHOO_VKEY_KPAD_0; break;
4591  case XK_KP_1: return WUHOO_VKEY_KPAD_1; break;
4592  case XK_KP_2: return WUHOO_VKEY_KPAD_2; break;
4593  case XK_KP_3: return WUHOO_VKEY_KPAD_3; break;
4594  case XK_KP_4: return WUHOO_VKEY_KPAD_4; break;
4595  case XK_KP_5: return WUHOO_VKEY_KPAD_5; break;
4596  case XK_KP_6: return WUHOO_VKEY_KPAD_6; break;
4597  case XK_KP_7: return WUHOO_VKEY_KPAD_7; break;
4598  case XK_KP_8: return WUHOO_VKEY_KPAD_8; break;
4599  case XK_KP_9: return WUHOO_VKEY_KPAD_9; break;
4600  case XK_0: return WUHOO_VKEY_0; break;
4601  case XK_1: return WUHOO_VKEY_1; break;
4602  case XK_2: return WUHOO_VKEY_2; break;
4603  case XK_3: return WUHOO_VKEY_3; break;
4604  case XK_4: return WUHOO_VKEY_4; break;
4605  case XK_5: return WUHOO_VKEY_5; break;
4606  case XK_6: return WUHOO_VKEY_6; break;
4607  case XK_7: return WUHOO_VKEY_7; break;
4608  case XK_8: return WUHOO_VKEY_8; break;
4609  case XK_9: return WUHOO_VKEY_9; break;
4610  case XK_A: return WUHOO_VKEY_A; break;
4611  case XK_B: return WUHOO_VKEY_B; break;
4612  case XK_C: return WUHOO_VKEY_C; break;
4613  case XK_D: return WUHOO_VKEY_D; break;
4614  case XK_E: return WUHOO_VKEY_E; break;
4615  case XK_F: return WUHOO_VKEY_F; break;
4616  case XK_G: return WUHOO_VKEY_G; break;
4617  case XK_H: return WUHOO_VKEY_H; break;
4618  case XK_I: return WUHOO_VKEY_I; break;
4619  case XK_J: return WUHOO_VKEY_J; break;
4620  case XK_K: return WUHOO_VKEY_K; break;
4621  case XK_L: return WUHOO_VKEY_L; break;
4622  case XK_M: return WUHOO_VKEY_M; break;
4623  case XK_N: return WUHOO_VKEY_N; break;
4624  case XK_O: return WUHOO_VKEY_O; break;
4625  case XK_P: return WUHOO_VKEY_P; break;
4626  case XK_Q: return WUHOO_VKEY_Q; break;
4627  case XK_R: return WUHOO_VKEY_R; break;
4628  case XK_S: return WUHOO_VKEY_S; break;
4629  case XK_T: return WUHOO_VKEY_T; break;
4630  case XK_U: return WUHOO_VKEY_U; break;
4631  case XK_V: return WUHOO_VKEY_V; break;
4632  case XK_W: return WUHOO_VKEY_W; break;
4633  case XK_X: return WUHOO_VKEY_X; break;
4634  case XK_Y: return WUHOO_VKEY_Y; break;
4635  case XK_Z: return WUHOO_VKEY_Z; break;
4636  case XK_Left: return WUHOO_VKEY_LEFT; break;
4637  case XK_Right: return WUHOO_VKEY_RIGHT; break;
4638  case XK_Up: return WUHOO_VKEY_UP; break;
4639  case XK_Down: return WUHOO_VKEY_DOWN; break;
4640  case XK_Escape: return WUHOO_VKEY_ESCAPE; break;
4641  case XK_Return: return WUHOO_VKEY_ENTER; break;
4642  case XK_Tab: return WUHOO_VKEY_TAB; break;
4643  case XK_space: return WUHOO_VKEY_SPACE; break;
4644  case XK_Delete: return WUHOO_VKEY_DELETE; break;
4645  case XK_Shift_R: return WUHOO_VKEY_SHIFT; break;
4646  case XK_Shift_L: return WUHOO_VKEY_SHIFT; break;
4647  case XK_Alt_L: return WUHOO_VKEY_ALT; break;
4648  case XK_Alt_R: return WUHOO_VKEY_ALT; break;
4649  case XK_Caps_Lock: return WUHOO_VKEY_CAPS_LOCK; break;
4650  case XK_Control_R: return WUHOO_VKEY_CONTROL; break;
4651  case XK_Control_L: return WUHOO_VKEY_CONTROL; break;
4652  case XK_F1: return WUHOO_VKEY_F1; break;
4653  case XK_F2: return WUHOO_VKEY_F2; break;
4654  case XK_F3: return WUHOO_VKEY_F3; break;
4655  case XK_F4: return WUHOO_VKEY_F4; break;
4656  case XK_F5: return WUHOO_VKEY_F5; break;
4657  case XK_F6: return WUHOO_VKEY_F6; break;
4658  case XK_F7: return WUHOO_VKEY_F7; break;
4659  case XK_F8: return WUHOO_VKEY_F8; break;
4660  case XK_F9: return WUHOO_VKEY_F9; break;
4661  case XK_F10: return WUHOO_VKEY_F10; break;
4662  case XK_F11: return WUHOO_VKEY_F11; break;
4663  case XK_F12: return WUHOO_VKEY_F12; break;
4664  case XK_Insert: return WUHOO_VKEY_INSERT; break;
4665  case XK_Home: return WUHOO_VKEY_HOME; break;
4666  case XK_Page_Up: return WUHOO_VKEY_PAGE_UP; break;
4667  case XK_BackSpace: WUHOO_VKEY_BACKSPACE; break;
4668  case XK_End: return WUHOO_VKEY_END; break;
4669  case XK_Page_Down: return WUHOO_VKEY_PAGE_DOWN; break;
4670 
4671  default: return WUHOO_VKEY_UNKNOWN; break;
4672  }
4673 
4674  return WUHOO_VKEY_UNKNOWN;
4675 }
4676 
4677 /* Find text-uri-list in a list of targets and return it's atom
4678  if available, else return None */
4679 WuhooInternal Atom
4680 _WuhooPickTextTargetX11(Display* disp, Atom* list, int list_count)
4681 {
4682  Atom request = None;
4683  char* name;
4684  int i;
4685  for (i = 0; i < list_count && request == None; i++) {
4686  name = XGetAtomName(disp, list[i]);
4687 
4688  if (WuhooTrue == WuhooStringCmp("text/plain;charset=utf-8", name, 100)) {
4689 
4690  request = list[i];
4691  }
4692  XFree(name);
4693  }
4694 
4695  return request;
4696 }
4697 
4698 WuhooInternal Atom
4699 _WuhooPickTextTargetFromThreeX11(Display* disp, Atom a0, Atom a1, Atom a2)
4700 {
4701  int count = 0;
4702  Atom atom[3];
4703  if (a0 != None)
4704  atom[count++] = a0;
4705  if (a1 != None)
4706  atom[count++] = a1;
4707  if (a2 != None)
4708  atom[count++] = a2;
4709  return _WuhooPickTextTargetX11(disp, atom, count);
4710 }
4711 
4712 WuhooInternal WuhooBoolean
4713 _WuhooIsXDigit(char c)
4714 {
4715  return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
4716  (c >= 'A' && c <= 'F'));
4717 }
4718 
4719 WuhooInternal void
4720 _WuhooURIDecode(char* dst, const char* src)
4721 {
4722  char a, b;
4723  while (*src) {
4724  if ((*src == '%') && ((a = src[1]) && (b = src[2])) &&
4725  (_WuhooIsXDigit(a) && _WuhooIsXDigit(b))) {
4726  if (a >= 'a')
4727  a -= 'a' - 'A';
4728  if (a >= 'A')
4729  a -= ('A' - 10);
4730  else
4731  a -= '0';
4732  if (b >= 'a')
4733  b -= 'a' - 'A';
4734  if (b >= 'A')
4735  b -= ('A' - 10);
4736  else
4737  b -= '0';
4738  *dst++ = 16 * a + b;
4739  src += 3;
4740  } else if (*src == '+') {
4741  *dst++ = ' ';
4742  src++;
4743  } else {
4744  *dst++ = *src++;
4745  }
4746  }
4747  *dst++ = '\0';
4748 }
4749 
4750 WuhooInternal WuhooResult
4751 _WuhooWindowDropContentsGetX11(WuhooWindow* window, WuhooEvent* event,
4752  char* buffer, int buffer_size)
4753 {
4754  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
4755  WuhooZeroInit(buffer, buffer_size);
4756 
4757  WuhooPropertyX11 p;
4758  _WuhooReadPropertyX11(&p, x11_window->display, x11_window->window,
4759  (Atom)event->data.drop.context);
4760 
4761  char* at = (char*)p.data;
4762  char* end = at;
4763  while (*end) {
4764  end++;
4765  }
4766  end--;
4767  while (*end == 10 || *end == 13) {
4768  end--;
4769  }
4770 
4771  end++;
4772 
4773  int count = 0;
4774  char* memory_offset = buffer;
4775  int index = 0;
4776  for (index = 0; index < event->data.drop.count; index++) {
4777  at += sizeof("file://") - 1;
4778  while (*at != 10 && *at != 13) {
4779  *memory_offset++ = *at++;
4780  }
4781  *memory_offset++ = '\n';
4782  while (*at == 10 || *at == 13) {
4783  at++;
4784  }
4785  }
4786 
4787  *memory_offset = 0;
4788 
4789  _WuhooURIDecode(buffer, buffer);
4790 
4791  XFree(p.data);
4792  XDeleteProperty(x11_window->display, x11_window->window,
4793  (Atom)event->data.drop.context);
4794 
4795  return WuhooSuccess;
4796 }
4797 
4798 WuhooInternal void
4799 _WuhooWindowEventGobbleX11(WuhooWindow* window, int event_type)
4800 {
4801  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
4802  XEvent dummy;
4803  while (True == XCheckTypedEvent(x11_window->display, event_type, &dummy)) {
4804  }
4805 }
4806 
4807 WuhooResult
4808 _WuhooWindowEventNextX11(WuhooWindow* window, WuhooEvent* event)
4809 {
4810  WuhooWindowX11* x11_window = (WuhooWindowX11*)window->platform_window;
4811 
4812  XEvent xevent;
4813 
4814  if (XPending(x11_window->display) == 0) {
4815  return WuhooSuccess;
4816  }
4817 
4818  XNextEvent(x11_window->display, &xevent);
4819  if (Expose == xevent.type)
4820  _WuhooWindowEventGobbleX11(window, Expose);
4821  if (MotionNotify == xevent.type)
4822  _WuhooWindowEventGobbleX11(window, MotionNotify);
4823  if (True == XFilterEvent(&xevent, None)) {
4824  XNextEvent(x11_window->display, &xevent);
4825  }
4826 
4827  switch (xevent.type) {
4828  case FocusIn: {
4829  window->window_flags |= WUHOO_WINDOW_FLAG_FOCUS_GAINED;
4830  break;
4831  }
4832  case FocusOut: {
4833  window->window_flags |= WUHOO_WINDOW_FLAG_FOCUS_GAINED;
4834  break;
4835  }
4836  case Expose: {
4837  /* https://tronche.com/gui/x/xlib/events/exposure/expose.html */
4838  /* Careful with how many expensive "expose" events we trigger */
4839  XExposeEvent* xexpose = &xevent.xexpose;
4840  window->window_flags |=
4841  (xexpose->count < 5) ? WUHOO_WINDOW_FLAG_REGION_UPDATED : 0;
4842  break;
4843  }
4844  case ConfigureNotify: {
4845  XConfigureEvent* xce = &xevent.xconfigure;
4846 
4847  /* This event type is generated for a variety of
4848  happenings, so check whether the window has been
4849  resized. */
4850 
4851  if (xce->x != x11_window->prev_x || xce->y != x11_window->prev_y) {
4852  x11_window->prev_x = xce->x;
4853  x11_window->prev_y = xce->y;
4854 
4855  int x, y;
4856  Window child;
4857  XWindowAttributes xwa;
4858  XTranslateCoordinates(x11_window->display, x11_window->window,
4859  x11_window->root, 0, 0, &x, &y, &child);
4860  XGetWindowAttributes(x11_window->display, x11_window->window, &xwa);
4861 
4862  window->x = x - xwa.x;
4863  window->y = y - xwa.y;
4864 
4865  _WuhooWindowStateX11(window);
4866  window->window_flags |= WUHOO_WINDOW_FLAG_MOVED;
4867  }
4868 
4869  if (xce->width != window->cwidth || xce->height != window->cheight) {
4870 
4871  int x, y;
4872  Window child;
4873  XWindowAttributes xwa;
4874  XTranslateCoordinates(x11_window->display, x11_window->window,
4875  x11_window->root, 0, 0, &x, &y, &child);
4876  XGetWindowAttributes(x11_window->display, x11_window->window, &xwa);
4877 
4878  window->cwidth = xce->width;
4879  window->cheight = xce->height;
4880 
4881  window->width = window->cwidth;
4882  window->height = window->cheight + x11_window->title_extents;
4883 
4884  _WuhooWindowStateX11(window);
4885  window->window_flags |= WUHOO_WINDOW_FLAG_RESIZED;
4886  }
4887  } break;
4888  case SelectionNotify: {
4889  XSelectionEvent* sev = (XSelectionEvent*)&xevent.xselection;
4890  if (sev->property == None)
4891  break;
4892 
4893  WuhooPropertyX11 p;
4894  _WuhooReadPropertyX11(&p, x11_window->display, x11_window->window,
4895  sev->property);
4896 
4897  char* at = (char*)p.data;
4898  int count = 0;
4899  int bytes_required = 0;
4900  while (*at) {
4901  if (*at == 10) {
4902  at++;
4903  continue;
4904  }
4905  count += (*at == 13) ? 1 : 0;
4906  bytes_required += (*at != 13) ? 1 : 0;
4907  at++;
4908  }
4909 
4910  event->data.drop.context = (WuhooHandle)sev->property;
4911  event->data.drop.size =
4912  bytes_required + 1 - (sizeof("file://") - 1) * count + 1;
4913  event->data.drop.count = count;
4914  event->type = WUHOO_EVT_DROP;
4915 
4916  XFree(p.data);
4917 
4918  } break;
4919  case ClientMessage: {
4920  if (xevent.xclient.data.l[0] == x11_window->wmDeleteMessage) {
4921  window->window_flags |= WUHOO_WINDOW_FLAG_CLOSED;
4922  window->is_alive = WuhooFalse;
4923 
4924  break;
4925  } else if (xevent.xclient.message_type == x11_window->XA_XdndDrop) {
4926  if (x11_window->XA_XdndReq == None) {
4927  /* say again - not interested! */
4928  XClientMessageEvent m;
4929  /* reply with status */
4930  WuhooZeroInit(&m, sizeof(XClientMessageEvent));
4931  m.type = ClientMessage;
4932  m.display = xevent.xclient.display;
4933  m.window = xevent.xclient.data.l[0];
4934  m.message_type = x11_window->XA_XdndFinished;
4935  m.format = 32;
4936  m.data.l[0] = x11_window->window;
4937  m.data.l[1] = 0;
4938  m.data.l[2] = None; /* fail! */
4939  XSendEvent(x11_window->display, xevent.xclient.data.l[0], False,
4940  NoEventMask, (XEvent*)&m);
4941  XFlush(x11_window->display);
4942  } else {
4943  /* convert */
4944  if (x11_window->XdndVersion >= 1) {
4945  XConvertSelection(x11_window->display, x11_window->XA_XdndSelection,
4946  x11_window->XA_XdndReq, XA_PRIMARY,
4947  x11_window->window, xevent.xclient.data.l[2]);
4948  } else {
4949  XConvertSelection(x11_window->display, x11_window->XA_XdndSelection,
4950  x11_window->XA_XdndReq, XA_PRIMARY,
4951  x11_window->window, CurrentTime);
4952  }
4953  }
4954  } else if (xevent.xclient.message_type == x11_window->XA_XdndPosition) {
4955 
4956  } else if (xevent.xclient.message_type == x11_window->XA_XdndEnter) {
4957  WuhooBoolean use_list = xevent.xclient.data.l[1] & 1;
4958  if (use_list) {
4959  WuhooPropertyX11 p;
4960  _WuhooReadPropertyX11(&p, x11_window->display,
4961  xevent.xclient.data.l[0],
4962  x11_window->XA_XdndTypeList);
4963  /* pick one */
4964  x11_window->XA_XdndReq = _WuhooPickTextTargetX11(
4965  x11_window->display, (Atom*)p.data, p.count);
4966  XFree(p.data);
4967  } else {
4968  /* pick from list of three */
4969  x11_window->XA_XdndReq = _WuhooPickTextTargetFromThreeX11(
4970  x11_window->display, xevent.xclient.data.l[2],
4971  xevent.xclient.data.l[3], xevent.xclient.data.l[4]);
4972  }
4973 
4974  XClientMessageEvent m;
4975  /* reply with status */
4976  WuhooZeroInit(&m, sizeof(XClientMessageEvent));
4977  m.type = ClientMessage;
4978  m.display = xevent.xclient.display;
4979  m.window = xevent.xclient.data.l[0];
4980  m.message_type = x11_window->XA_XdndStatus;
4981  m.format = 32;
4982  m.data.l[0] = x11_window->window;
4983  m.data.l[1] = (x11_window->XA_XdndReq != None);
4984  m.data.l[2] = 0; /* specify an empty rectangle */
4985  m.data.l[3] = 0;
4986  m.data.l[4] =
4987  x11_window->XA_XdndActionCopy; /* we only accept copying anyway */
4988 
4989  XSendEvent(x11_window->display, xevent.xclient.data.l[0], False,
4990  NoEventMask, (XEvent*)&m);
4991  XFlush(x11_window->display);
4992  }
4993  } break;
4994  case MotionNotify: {
4995  event->type = WUHOO_EVT_MOUSE_MOVE;
4996  event->data.mouse_move.x = xevent.xmotion.x;
4997  event->data.mouse_move.y = xevent.xmotion.y;
4998  event->data.mouse_move.state = WUHOO_MSTATE_UNKNOWN;
4999 
5000  if (xevent.xmotion.state & Button1MotionMask)
5001  event->data.mouse_move.state = WUHOO_MSTATE_LPRESSED;
5002  else if (xevent.xmotion.state & Button2MotionMask)
5003  event->data.mouse_move.state = WUHOO_MSTATE_MPRESSED;
5004  else if (xevent.xmotion.state & Button3MotionMask)
5005  event->data.mouse_move.state = WUHOO_MSTATE_RPRESSED;
5006  event->data.mouse_move.mods |=
5007  (xevent.xmotion.state & ShiftMask) ? WUHOO_KMOD_SHIFT : 0;
5008  event->data.mouse_move.mods |=
5009  (xevent.xmotion.state & ControlMask) ? WUHOO_KMOD_CTRL : 0;
5010  event->data.mouse_move.mods |=
5011  (xevent.xmotion.state & LockMask) ? WUHOO_KMOD_CAPS : 0;
5012  } break;
5013  case ButtonPress: {
5014  if (Button4 == xevent.xbutton.button ||
5015  Button5 == xevent.xbutton.button) {
5016  event->type = WUHOO_EVT_MOUSE_WHEEL;
5017  event->data.mouse_wheel.mods |=
5018  (xevent.xbutton.state & ShiftMask) ? WUHOO_KMOD_SHIFT : 0;
5019  event->data.mouse_wheel.mods |=
5020  (xevent.xbutton.state & ControlMask) ? WUHOO_KMOD_CTRL : 0;
5021  event->data.mouse_wheel.mods |=
5022  (xevent.xbutton.state & LockMask) ? WUHOO_KMOD_CAPS : 0;
5023  event->data.mouse_wheel.x = xevent.xbutton.x;
5024  event->data.mouse_wheel.y = xevent.xbutton.y;
5025  event->data.mouse_wheel.delta_y =
5026  (Button4 == xevent.xbutton.button) ? 1.0f : -1.0f;
5027 
5028  break;
5029  }
5030 
5031  event->type = WUHOO_EVT_MOUSE_PRESS;
5032  event->data.mouse_press.click_count = 1;
5033  event->data.mouse_press.mods |=
5034  (xevent.xbutton.state & ShiftMask) ? WUHOO_KMOD_SHIFT : 0;
5035  event->data.mouse_press.mods |=
5036  (xevent.xbutton.state & ControlMask) ? WUHOO_KMOD_CTRL : 0;
5037  event->data.mouse_press.mods |=
5038  (xevent.xbutton.state & LockMask) ? WUHOO_KMOD_CAPS : 0;
5039  if (Button1 == xevent.xbutton.button)
5040  event->data.mouse_press.state = WUHOO_MSTATE_LPRESSED;
5041  else if (Button2 == xevent.xbutton.button)
5042  event->data.mouse_press.state = WUHOO_MSTATE_MPRESSED;
5043  else if (Button3 == xevent.xbutton.button)
5044  event->data.mouse_press.state = WUHOO_MSTATE_RPRESSED;
5045 
5046  } break;
5047  case ButtonRelease: {
5048  if (Button4 == xevent.xbutton.button || Button5 == xevent.xbutton.button)
5049  break;
5050 
5051  event->type = WUHOO_EVT_MOUSE_PRESS;
5052  event->data.mouse_press.click_count = 1;
5053  if (Button1 == xevent.xbutton.button)
5054  event->data.mouse_press.state = WUHOO_MSTATE_LRELEASED;
5055  else if (Button2 == xevent.xbutton.button)
5056  event->data.mouse_press.state = WUHOO_MSTATE_RRELEASED;
5057  else if (Button3 == xevent.xbutton.button)
5058  event->data.mouse_press.state = WUHOO_MSTATE_MRELEASED;
5059  event->data.mouse_press.mods |=
5060  (xevent.xbutton.state & ShiftMask) ? WUHOO_KMOD_SHIFT : 0;
5061  event->data.mouse_press.mods |=
5062  (xevent.xbutton.state & ControlMask) ? WUHOO_KMOD_CTRL : 0;
5063  event->data.mouse_press.mods |=
5064  (xevent.xbutton.state & LockMask) ? WUHOO_KMOD_CAPS : 0;
5065  } break;
5066  case KeyRelease: {
5067  KeySym ksym;
5068  Status status;
5069  char buff[16];
5070 
5071  /* Override this in order for Xutf8LookupString to work */
5072  xevent.xkey.type = KeyPress;
5073  size_t c = Xutf8LookupString(x11_window->ic, &xevent.xkey, buff, 16 - 1,
5074  &ksym, &status);
5075 
5076  buff[c] = 0;
5077 
5078  event->type = WUHOO_EVT_KEY;
5079  WuhooStringCopy((char*)event->data.key.character, buff, c);
5080  event->data.key.state = WUHOO_KSTATE_UP;
5081  event->data.key.code = _WuhooKeyTranslateX11(ksym);
5082  event->data.key.mods |=
5083  (xevent.xkey.state & ShiftMask) ? WUHOO_KMOD_SHIFT : 0;
5084  event->data.key.mods |=
5085  (xevent.xkey.state & ControlMask) ? WUHOO_KMOD_CTRL : 0;
5086  event->data.key.mods |=
5087  (xevent.xkey.state & LockMask) ? WUHOO_KMOD_CAPS : 0;
5088 
5089  break;
5090  }
5091  case KeyPress: {
5092  KeySym ksym;
5093  Status status;
5094  char buff[16];
5095 
5096  size_t c = Xutf8LookupString(x11_window->ic, &xevent.xkey, buff, 16 - 1,
5097  &ksym, &status);
5098 
5099  buff[c] = 0;
5100 
5101  event->type = WUHOO_EVT_KEY;
5102  WuhooStringCopy((char*)event->data.key.character, buff, c);
5103  event->data.key.state = WUHOO_KSTATE_DOWN;
5104  event->data.key.code = _WuhooKeyTranslateX11(ksym);
5105  event->data.key.mods |=
5106  (xevent.xkey.state & ShiftMask) ? WUHOO_KMOD_SHIFT : 0;
5107  event->data.key.mods |=
5108  (xevent.xkey.state & ControlMask) ? WUHOO_KMOD_CTRL : 0;
5109  event->data.key.mods |=
5110  (xevent.xkey.state & LockMask) ? WUHOO_KMOD_CAPS : 0;
5111 
5112  break;
5113  }
5114  default:
5115  // XFlush(x11_window->display);
5116  break;
5117  }
5118 
5119  if (window->window_flags & WUHOO_WINDOW_FLAG_CLOSED) {
5120  event->type = WUHOO_EVT_WINDOW;
5121  event->data.window.state = WUHOO_WSTATE_CLOSED;
5122 
5123  return WuhooSuccess;
5124  } else if (window->window_flags & WUHOO_WINDOW_FLAG_RESIZED) {
5125  event->type = WUHOO_EVT_WINDOW;
5126  event->data.window.state = WUHOO_WSTATE_RESIZED;
5127  event->data.window.data1 = window->cwidth;
5128  event->data.window.data2 = window->cheight;
5129  event->data.window.flags = WUHOO_WINDOW_FLAG_RESIZED;
5130  event->data.window.flags |= (window->window_flags & WUHOO_WINDOW_FLAG_MAXIMIZED) ? WUHOO_WINDOW_FLAG_MAXIMIZED : 0;
5131  event->data.window.flags |= (window->window_flags & WUHOO_WINDOW_FLAG_FULL_SCREEN) ? WUHOO_WINDOW_FLAG_FULL_SCREEN : 0;
5132  event->data.window.flags |= (window->window_flags & WUHOO_WINDOW_FLAG_MINIMIZED) ? WUHOO_WINDOW_FLAG_MINIMIZED : 0;
5133 
5134  window->window_flags &= ~WUHOO_WINDOW_FLAG_RESIZED;
5135  window->window_flags &= ~WUHOO_WINDOW_FLAG_MAXIMIZED;
5136  window->window_flags &= ~WUHOO_WINDOW_FLAG_MINIMIZED;
5137  window->window_flags &= ~WUHOO_WINDOW_FLAG_FULL_SCREEN;
5138 
5139  return WuhooSuccess;
5140  } else if (window->window_flags & WUHOO_WINDOW_FLAG_MOVED) {
5141  event->type = WUHOO_EVT_WINDOW;
5142  event->data.window.state = WUHOO_WSTATE_MOVED;
5143  event->data.window.data1 = window->x;
5144  event->data.window.data2 = window->y;
5145  window->window_flags &= ~WUHOO_WINDOW_FLAG_MOVED;
5146 
5147  return WuhooSuccess;
5148  } else if (window->window_flags & WUHOO_WINDOW_FLAG_FOCUS_LOST) {
5149  event->type = WUHOO_EVT_WINDOW;
5150  event->data.window.state = WUHOO_WSTATE_UNFOCUSED;
5151  event->data.window.data1 = window->cwidth;
5152  event->data.window.data2 = window->cheight;
5153  window->window_flags &= ~WUHOO_WINDOW_FLAG_FOCUS_LOST;
5154 
5155  return WuhooSuccess;
5156  } else if (window->window_flags & WUHOO_WINDOW_FLAG_FOCUS_GAINED) {
5157  event->type = WUHOO_EVT_WINDOW;
5158  event->data.window.state = WUHOO_WSTATE_FOCUSED;
5159  event->data.window.data1 = window->cwidth;
5160  event->data.window.data2 = window->cheight;
5161  window->window_flags &= ~WUHOO_WINDOW_FLAG_FOCUS_GAINED;
5162 
5163  return WuhooSuccess;
5164  } else if (window->window_flags & WUHOO_WINDOW_FLAG_REGION_UPDATED) {
5165  event->type = WUHOO_EVT_WINDOW;
5166  event->data.window.state = WUHOO_WSTATE_INVALIDATED;
5167  event->data.window.data1 = window->cwidth;
5168  event->data.window.data2 = window->cheight;
5169  window->window_flags &= ~WUHOO_WINDOW_FLAG_REGION_UPDATED;
5170 
5171  return WuhooSuccess;
5172  }
5173 
5174  return WuhooSuccess;
5175 }
5176 
5177 #endif /* WUHOO_X11_ENABLE */
5178 
5179 WuhooResult
5181 {
5182  WuhooZeroInit(window, sizeof(*window));
5183 
5184 #ifdef WUHOO_OPENGL_ENABLE
5185  window->gl_framebuffer.version.major = 4;
5186  window->gl_framebuffer.version.minor = 0;
5187  window->gl_framebuffer.redBits = 3;
5188  window->gl_framebuffer.greenBits = 3;
5189  window->gl_framebuffer.blueBits = 2;
5190  window->gl_framebuffer.alphaBits = 0;
5191  window->gl_framebuffer.depthBits = 16;
5192  window->gl_framebuffer.stencilBits = 0;
5193  window->gl_framebuffer.accumRedBits = 0;
5194  window->gl_framebuffer.accumGreenBits = 0;
5195  window->gl_framebuffer.accumBlueBits = 0;
5196  window->gl_framebuffer.accumAlphaBits = 0;
5197  window->gl_framebuffer.auxBuffers = 0;
5198  window->gl_framebuffer.stereo = WuhooFalse;
5199  window->gl_framebuffer.samples = 0;
5200  window->gl_framebuffer.sRGB = WuhooFalse;
5201  window->gl_framebuffer.doublebuffer = WuhooTrue;
5202  window->gl_framebuffer.transparent = WuhooFalse;
5203 #endif
5204 
5205  return WuhooSuccess;
5206 }
5207 
5208 WuhooResult
5210 {
5211  WuhooResult result = WuhooSuccess;
5212 
5213 #ifdef _WIN32
5214  result = _WuhooWindowDestroyWin32(window);
5215 #endif
5216 
5217 #ifdef __APPLE__
5218  result = _WuhooWindowDestroyCocoa(window);
5219 #endif
5220 
5221 #ifdef WUHOO_X11_ENABLE
5222  result = _WuhooWindowDestroyX11(window);
5223 #endif
5224 
5225  return result;
5226 }
5227 WuhooResult
5228 WuhooWindowCreate(WuhooWindow* window, int posx, int posy, WuhooSize width,
5229  WuhooSize height, const char* title, WuhooFlags flags,
5230  const void* data)
5231 {
5232  WuhooResult result = WuhooSuccess;
5233 
5234  window->width = width;
5235  window->height = height;
5236  window->flags = flags;
5237  window->x = posx;
5238  window->y = posy;
5239 
5240 #ifdef _WIN32
5241  result =
5242  _WuhooWindowCreateWin32(window, posx, posy, width, height, title, flags);
5243 #endif
5244 
5245 #ifdef __APPLE__
5246  result =
5247  _WuhooWindowCreateCocoa(window, posx, posy, width, height, title, flags);
5248 #endif
5249 
5250 #ifdef WUHOO_X11_ENABLE
5251  result =
5252  _WuhooWindowCreateX11(window, posx, posy, width, height, title, flags);
5253 #endif
5254 
5255  window->is_initialized = (WuhooSuccess == result);
5256  window->is_alive = (WuhooSuccess == result);
5257 
5258  return result;
5259 }
5260 WuhooResult
5262 {
5263  WuhooZeroInit(event, sizeof(*event));
5264 
5265 #ifdef _WIN32
5266  return _WuhooWindowEventNextWin32(window, event);
5267 #endif
5268 #ifdef __APPLE__
5269  return _WuhooWindowEventNextCocoa(window, event);
5270 #endif
5271 #ifdef WUHOO_X11_ENABLE
5272  return _WuhooWindowEventNextX11(window, event);
5273 #endif
5274 }
5275 
5276 WuhooResult
5278 {
5279 #ifdef _WIN32
5280  return _WuhooWindowShowWin32(window);
5281 #endif
5282 #ifdef __APPLE__
5283  return _WuhooWindowShowCocoa(window);
5284 #endif
5285 #ifdef WUHOO_X11_ENABLE
5286  return _WuhooWindowShowX11(window);
5287 #endif
5288 }
5289 WuhooResult
5290 WuhooWindowRegionSet(WuhooWindow* window, int posx, int posy, WuhooSize width,
5291  WuhooSize height)
5292 {
5293 
5294  if (!(WUHOO_FLAG_RESIZEABLE & window->flags)) {
5295  return (WuhooResult)WUHOO_PLATFORM_API_STRING
5296  " : WUHOO_FLAG_RESIZEABLE is not set for this window";
5297  }
5298 
5299  /* Stick to lower left origin for everyone */
5300 #ifdef __APPLE__
5301  return _WuhooWindowRegionSetCocoa(window, posx, posy, width, height);
5302 #endif
5303 
5304 #ifdef _WIN32
5305  return _WuhooWindowRegionSetWin32(window, posx, posy, width, height);
5306 #endif
5307 
5308 #ifdef WUHOO_X11_ENABLE
5309  return _WuhooWindowRegionSetX11(window, posx, posy, width, height);
5310 #endif
5311 }
5312 WuhooResult
5313 WuhooWindowRegionGet(WuhooWindow* window, int* posx, int* posy,
5314  WuhooSize* width, WuhooSize* height)
5315 {
5316 #ifdef __APPLE__
5317  return _WuhooWindowRegionGetCocoa(window, posx, posy, width, height);
5318 #endif
5319 
5320 #ifdef _WIN32
5321  return _WuhooWindowRegionGetWin32(window, posx, posy, width, height);
5322 #endif
5323 
5324 #ifdef WUHOO_X11_ENABLE
5325  return _WuhooWindowRegionGetX11(window, posx, posy, width, height);
5326 #endif
5327 }
5328 WuhooResult
5329 WuhooWindowClientRegionGet(WuhooWindow* window, int* posx, int* posy,
5330  WuhooSize* width, WuhooSize* height)
5331 {
5332 #ifdef __APPLE__
5333  return _WuhooWindowClientRegionGetCocoa(window, posx, posy, width, height);
5334 #endif
5335 
5336 #ifdef _WIN32
5337  return _WuhooWindowClientRegionGetWin32(window, posx, posy, width, height);
5338 #endif
5339 
5340 #ifdef WUHOO_X11_ENABLE
5341  return _WuhooWindowClientRegionGetX11(window, posx, posy, width, height);
5342 #endif
5343 
5344  return WuhooSuccess;
5345 }
5346 WuhooResult
5347 WuhooWindowClientRegionSet(WuhooWindow* window, int posx, int posy,
5348  WuhooSize width, WuhooSize height)
5349 {
5350 #ifdef __APPLE__
5351  return _WuhooWindowClientRegionSetCocoa(window, posx, posy, width, height);
5352 #endif
5353 
5354 #ifdef _WIN32
5355  return _WuhooWindowClientRegionSetWin32(window, posx, posy, width, height);
5356 #endif
5357 
5358 #ifdef WUHOO_X11_ENABLE
5359  return _WuhooWindowClientRegionSetX11(window, posx, posy, width, height);
5360 #endif
5361 
5362  return WuhooSuccess;
5363 }
5364 WuhooResult
5365 WuhooWindowSetTitle(WuhooWindow* window, const char* title)
5366 {
5367  WuhooResult result = WuhooSuccess;
5368 
5369  if (WuhooNull == title)
5370  return (WuhooResult) "Invalid title";
5371 
5372  if ( !(WUHOO_FLAG_TITLED & window->flags) )
5373  return (WuhooResult) "WUHOO_FLAG_TITLED is not set";
5374 
5375 #ifdef __APPLE__
5376  result = _WuhooWindowSetTitleCocoa(window, title);
5377 #endif
5378 
5379 #ifdef _WIN32
5380  result = _WuhooWindowSetTitleWin32(window, title);
5381 #endif
5382 
5383 #ifdef WUHOO_X11_ENABLE
5384  result = _WuhooWindowSetTitleX11(window, title);
5385 #endif
5386 
5387  return result;
5388 }
5389 
5390 WuhooResult
5391 WuhooWindowDropContentsGet(WuhooWindow* window, WuhooEvent* event, char* buffer,
5392  WuhooSize buffer_size)
5393 {
5394  WuhooResult result = WuhooSuccess;
5395 
5396  if (WuhooNull == buffer) {
5397  return (WuhooResult)"invalid buffer passed.";
5398  }
5399 
5400  if (buffer_size < event->data.drop.size) {
5401  return (WuhooResult)"invalid buffer size.";
5402  }
5403 
5404 #ifdef __APPLE__
5405  result = _WuhooWindowDropContentsGetCocoa(window, event, buffer, buffer_size);
5406 #endif
5407 
5408 #ifdef _WIN32
5409  result = _WuhooWindowDropContentsGetWin32(window, event, buffer, buffer_size);
5410 #endif
5411 
5412 #ifdef WUHOO_X11_ENABLE
5413  result = _WuhooWindowDropContentsGetX11(window, event, buffer, buffer_size);
5414 #endif
5415 
5416  return result;
5417 }
5418 
5419 WuhooResult
5420 WuhooWindowBlit(WuhooWindow* window, WuhooRGBA* pixels, WuhooSize src_x,
5421  WuhooSize src_y, WuhooSize src_width, WuhooSize src_height,
5422  WuhooSize dst_x, WuhooSize dst_y, WuhooSize dst_width,
5423  WuhooSize dst_height)
5424 {
5425  WuhooResult result = WuhooSuccess;
5426 
5427 #ifdef __APPLE__
5428  result =
5429  _WuhooWindowBlitCocoa(window, pixels, dst_x, dst_y, dst_width, dst_height);
5430 #endif
5431 
5432 #ifdef _WIN32
5433  result =
5434  _WuhooWindowBlitWin32(window, pixels, dst_x, dst_y, dst_width, dst_height);
5435 #endif
5436 
5437 #ifdef WUHOO_X11_ENABLE
5438  result =
5439  _WuhooWindowBlitX11(window, pixels, dst_x, dst_y, dst_width, dst_height);
5440 #endif
5441 
5442  return result;
5443 }
5444 
5445 WuhooInternal void
5446 WuhooCopy(void* const to, void const* const from, WuhooSize count)
5447 {
5448  unsigned char* const char_view_to = (unsigned char* const)to;
5449  unsigned char const* const char_view_from = (unsigned char const* const)from;
5450  while (--count)
5451  char_view_to[count] = char_view_from[count];
5452  char_view_to[count] = char_view_from[count];
5453 }
5454 
5455 /* Color conversion kernels */
5456 WuhooMaybeUnused WuhooInternal WuhooResult
5457 WuhooConvertRGBANoOp(void* dst, WuhooRGBA const* const src, WuhooSize x,
5458  WuhooSize y, WuhooSize width, WuhooSize height,
5459  WuhooSize src_width, WuhooSize src_height)
5460 {
5461 
5462  return WuhooSuccess;
5463 }
5464 
5465 WuhooMaybeUnused WuhooInternal WuhooResult
5466 WuhooConvertRGBAtoRGBA(void* dst, WuhooRGBA const* const src, WuhooSize x,
5467  WuhooSize y, WuhooSize width, WuhooSize height,
5468  WuhooSize src_width, WuhooSize src_height)
5469 {
5470 
5471  WuhooCopy(dst, src, width * height * sizeof(*src));
5472 
5473  return WuhooSuccess;
5474 }
5475 
5476 WuhooMaybeUnused WuhooInternal WuhooResult
5477 WuhooConvertRGBAtoRGB(void* dst, WuhooRGBA const* const src, WuhooSize x,
5478  WuhooSize y, WuhooSize width, WuhooSize height,
5479  WuhooSize src_width, WuhooSize src_height)
5480 {
5481  WuhooResult result = WuhooSuccess;
5482  WuhooRGBA* rgba_dst = (WuhooRGBA*)dst;
5483 
5484  WuhooSize i = 0;
5485  for (i = 0; i < width * height; i++) {
5486  rgba_dst[i].r = src[i].b;
5487  rgba_dst[i].g = src[i].g;
5488  rgba_dst[i].b = src[i].r;
5489  rgba_dst[i].a = 0;
5490  }
5491 
5492  return result;
5493 }
5494 
5495 WuhooMaybeUnused WuhooInternal WuhooResult
5496 WuhooConvertRGBAtoBGRA(void* dst, WuhooRGBA const* const src, WuhooSize x,
5497  WuhooSize y, WuhooSize width, WuhooSize height,
5498  WuhooSize src_width, WuhooSize src_height)
5499 {
5500  WuhooResult result = WuhooSuccess;
5501  WuhooRGBA* rgba_dst = (WuhooRGBA*)dst;
5502 
5503  WuhooSize i = 0;
5504  for (i = 0; i < width * height; i++) {
5505  rgba_dst[i].r = src[i].b;
5506  rgba_dst[i].g = src[i].g;
5507  rgba_dst[i].b = src[i].r;
5508  rgba_dst[i].a = src[i].a;
5509  }
5510 
5511  return result;
5512 }
5513 
5514 WuhooMaybeUnused WuhooInternal WuhooResult
5515 WuhooConvertRGBAtoR5G6B5(void* dst, WuhooRGBA const* const src, WuhooSize dst_x,
5516  WuhooSize dst_y, WuhooSize dst_width,
5517  WuhooSize dst_height, WuhooSize src_width,
5518  WuhooSize src_height)
5519 {
5520  WuhooResult result = WuhooSuccess;
5521 
5522  /* https://stackoverflow.com/questions/42388721/x11-graphics-rendering-improvement
5523  */
5524  /* Why do we need to go off by one */
5525  WuhooSize i = 0, j = 0;
5526  for (i = 0; i < dst_width; ++i) {
5527  for (j = 0; j < dst_height; ++j) {
5528  WuhooSize pixel_index = i + j * src_width;
5529  WuhooSize pixel_index_relative = i + j * (dst_width + (dst_width & 1));
5530 
5531  unsigned short out =
5532  (unsigned char)(((float)src[pixel_index].r / 255.0f) * 31.0f);
5533  unsigned short outg =
5534  (unsigned char)(((float)src[pixel_index].g / 255.0f) * 63.0f);
5535 
5536  unsigned short pixel_val = (out << 11) | (outg << 5) | (out << 0);
5537 
5538  ((unsigned short*)dst)[pixel_index_relative] = pixel_val;
5539  //((unsigned char*)dst)[2 * pixel_index + 0] = (unsigned char)(pixel_val &
5540  // 0x00FF);
5541  //((unsigned char*)dst)[2 * pixel_index + 1] = (unsigned char)((pixel_val
5542  //>> 8) & 0x00FF);
5543  }
5544  }
5545 
5546  return result;
5547 }
5548 
5549 /* Helpers and utilities */
5550 WuhooMaybeUnused WuhooInternal void
5551 WuhooCharacterCopy(char* to, const char* from)
5552 {
5553  while ('\0' != *from) {
5554  *to++ = *from++;
5555  }
5556  *to = 0;
5557 }
5558 
5559 WuhooMaybeUnused WuhooInternal void
5560 WuhooZeroInit(void* to, WuhooSize count)
5561 {
5562  unsigned char* char_view = (unsigned char*)to;
5563  while (--count)
5564  char_view[count] = 0;
5565  char_view[count] = 0;
5566 }
5567 
5568 WuhooMaybeUnused WuhooInternal WuhooSize
5569 WuhooStringCopy(char* to, const char* from, WuhooSize max_count)
5570 {
5571  WuhooSize count = max_count;
5572  while ('\0' != *from && (count > 0)) {
5573  *to++ = *from++;
5574  count--;
5575  }
5576  *to = 0;
5577  return (max_count - count);
5578 }
5579 
5580 WuhooMaybeUnused WuhooInternal WuhooBoolean
5581 WuhooStringCmp(const char* to, const char* from, WuhooSize max_count)
5582 {
5583  WuhooSize count = max_count;
5584 
5585  while (0 != *from && 0 != *to && (count > 0)) {
5586  if (*to != *from) {
5587  return WuhooFalse;
5588  }
5589 
5590  to++;
5591  from++;
5592  count--;
5593  }
5594 
5595  return WuhooTrue;
5596 }
5597 
5598 WuhooMaybeUnused WuhooInternal int
5599 WuhooMini(int a, int b)
5600 {
5601  return (a < b) ? a : b;
5602 }
5603 WuhooMaybeUnused WuhooInternal int
5604 WuhooMaxi(int a, int b)
5605 {
5606  return !(a < b) ? a : b;
5607 }
5608 WuhooMaybeUnused WuhooInternal void
5609 WuhooMemzero(void* address, WuhooSize size)
5610 {
5611  while (size--) {
5612  ((WuhooByte*)address)[size] = 0;
5613  }
5614 }
5615 WuhooMaybeUnused WuhooSize
5616 WuhooStringLength(const char* str, WuhooSize max_count)
5617 {
5618  WuhooSize count = max_count;
5619  while ('\0' != *str && (count > 0)) {
5620  str++;
5621  count--;
5622  }
5623  return (max_count - count);
5624 }
5625 
5626 const char*
5627 WuhooResultString(WuhooResult result)
5628 {
5629  return (const char* const)(WuhooSuccess == result ? "Success" : result);
5630 }
5631 
5632 #endif /* WUHOO_IMPLEMENTATION */
WuhooMouseState
Definition: wuhoo.h:330
#define WuhooSuccess
Definition: wuhoo.h:22
WuhooResult WuhooWindowBlit(WuhooWindow *window, WuhooRGBA *pixels, WuhooSize src_x, WuhooSize src_y, WuhooSize src_width, WuhooSize src_height, WuhooSize dst_x, WuhooSize dst_y, WuhooSize dst_width, WuhooSize dst_height)
Definition: wuhoo.h:380
WuhooWindowState
Definition: wuhoo.h:273
WuhooEventDrop.
Definition: wuhoo.h:299
WuhooResult WuhooWindowSetTitle(WuhooWindow *window, const char *title)
Struct representing a keyboard press.
Definition: wuhoo.h:319
WuhooResult WuhooWindowClientRegionGet(WuhooWindow *window, int *posx, int *posy, WuhooSize *width, WuhooSize *height)
WuhooResult WuhooWindowInit(WuhooWindow *window)
unsigned char g
Definition: wuhoo.h:349
Definition: wuhoo.h:395
Definition: wuhoo.h:362
#define WUHOO_STRING
Definition: wuhoo.h:33
WuhooResult WuhooWindowShow(WuhooWindow *window)
WuhooResult WuhooWindowDropContentsGet(WuhooWindow *window, WuhooEvent *event, char *buffer, WuhooSize buffer_size)
Definition: wuhoo.h:371
WuhooEventKey.
Definition: wuhoo.h:295
WuhooEventWindow.
Definition: wuhoo.h:294
unsigned char b
Definition: wuhoo.h:350
WuhooResult WuhooWindowRegionSet(WuhooWindow *window, int posx, int posy, WuhooSize width, WuhooSize height)
Definition: wuhoo.h:354
unsigned char r
Definition: wuhoo.h:348
The event union that holds event-specific data.
Definition: wuhoo.h:408
WuhooResult WuhooWindowDestroy(WuhooWindow *window)
WuhooEventMouseMove.
Definition: wuhoo.h:297
WuhooKeyCode code
Definition: wuhoo.h:323
WuhooResult WuhooWindowRegionGet(WuhooWindow *window, int *posx, int *posy, WuhooSize *width, WuhooSize *height)
WuhooKeyModifiers mods
Definition: wuhoo.h:321
Helper struct to work with RGBA system backed buffers. The alpha channel is currently not respected i...
Definition: wuhoo.h:346
WuhooResult WuhooWindowClientRegionSet(WuhooWindow *window, int posx, int posy, WuhooSize width, WuhooSize height)
unsigned char a
Definition: wuhoo.h:351
const char * WuhooResultString(WuhooResult result)
Definition: wuhoo.h:449
WuhooEventMouseWheel.
Definition: wuhoo.h:298
WuhooResult WuhooWindowEventNext(WuhooWindow *window, WuhooEvent *event)
WuhooEventType
Definition: wuhoo.h:292
WuhooKeyState state
Definition: wuhoo.h:322
Definition: wuhoo.h:388
WuhooResult WuhooWindowCreate(WuhooWindow *window, int posx, int posy, WuhooSize width, WuhooSize height, const char *title, WuhooFlags flags, const void *data)
WuhooEventMousePress.
Definition: wuhoo.h:296