Mercurial > dwindows
comparison android/dw.cpp @ 2475:16d195d46f2a
Android: Implement dw_window_new(), dw_box_new() and dw_box_pack().
Initialize JNI when creating new threads.
Include jni.h from dw.h so we can type HWND as jobject.
Remove our own resize code, we are going to try to use LinearLayout.
author | bsmith@81767d24-ef19-dc11-ae90-00e081727c95 |
---|---|
date | Wed, 21 Apr 2021 11:15:26 +0000 |
parents | a13e6db064f4 |
children | 20c9e83cba2a |
comparison
equal
deleted
inserted
replaced
2474:a13e6db064f4 | 2475:16d195d46f2a |
---|---|
5 * (C) 2011-2021 Brian Smith <brian@dbsoft.org> | 5 * (C) 2011-2021 Brian Smith <brian@dbsoft.org> |
6 * (C) 2011-2021 Mark Hessling <mark@rexx.org> | 6 * (C) 2011-2021 Mark Hessling <mark@rexx.org> |
7 * | 7 * |
8 */ | 8 */ |
9 | 9 |
10 #include <jni.h> | |
11 #include "dw.h" | 10 #include "dw.h" |
12 #include <stdlib.h> | 11 #include <stdlib.h> |
13 #include <string.h> | 12 #include <string.h> |
14 #include <sys/utsname.h> | 13 #include <sys/utsname.h> |
15 #include <sys/socket.h> | 14 #include <sys/socket.h> |
35 | 34 |
36 #ifdef __cplusplus | 35 #ifdef __cplusplus |
37 extern "C" { | 36 extern "C" { |
38 #endif | 37 #endif |
39 | 38 |
40 static pthread_key_t _dw_event_key; | 39 static pthread_key_t _dw_env_key; |
41 static HEV _dw_main_event; | 40 static HEV _dw_main_event; |
41 static JavaVM *_dw_jvm; | |
42 static jobject _dw_obj; | |
42 | 43 |
43 /* Call the dwmain entry point, Android has no args, so just pass the app path */ | 44 /* Call the dwmain entry point, Android has no args, so just pass the app path */ |
44 void _dw_main_launch(char *arg) | 45 void _dw_main_launch(char *arg) |
45 { | 46 { |
46 char *argv[2] = { arg, NULL }; | 47 char *argv[2] = { arg, NULL }; |
56 JNIEXPORT jstring JNICALL | 57 JNIEXPORT jstring JNICALL |
57 Java_org_dbsoft_dwindows_dwtest_DWindows_dwindowsInit( | 58 Java_org_dbsoft_dwindows_dwtest_DWindows_dwindowsInit( |
58 JNIEnv* env, jobject obj, jstring path) { | 59 JNIEnv* env, jobject obj, jstring path) { |
59 char *arg = strdup(env->GetStringUTFChars((jstring) path, NULL)); | 60 char *arg = strdup(env->GetStringUTFChars((jstring) path, NULL)); |
60 | 61 |
62 /* Initialize the handle to the Java Virtual Machine */ | |
63 env->GetJavaVM(&_dw_jvm); | |
64 _dw_obj = obj; | |
65 | |
66 /* Save the JNIEnv for the main thread */ | |
67 pthread_setspecific(_dw_env_key, env); | |
68 | |
61 /* Create the dwmain event */ | 69 /* Create the dwmain event */ |
62 _dw_main_event = dw_event_new(); | 70 _dw_main_event = dw_event_new(); |
63 | 71 |
64 /* Launch the new thread to execute dwmain() */ | 72 /* Launch the new thread to execute dwmain() */ |
65 dw_thread_new((void *)_dw_main_launch, arg, 0); | 73 dw_thread_new((void *)_dw_main_launch, arg, 0); |
66 | 74 |
67 /* Wait until dwmain() calls dw_main() then return */ | 75 /* Wait until dwmain() calls dw_main() then return */ |
68 dw_event_wait(_dw_main_event, DW_TIMEOUT_INFINITE); | 76 dw_event_wait(_dw_main_event, DW_TIMEOUT_INFINITE); |
69 #if 0 | |
70 // Construct a String | |
71 jstring jstr = env->NewStringUTF("This string comes from JNI"); | |
72 // First get the class that contains the method you need to call | |
73 jclass clazz = env->FindClass("org/dbsoft/dwindows/dwtest/DWindows"); | |
74 // Get the method that you want to call | |
75 jmethodID dwindowsInit = env->GetMethodID(clazz, "dwindowsInit", "(Ljava/lang/String;)V"); | |
76 // Call the method on the object | |
77 jobject result = env->CallObjectMethod(obj, jstr, dwindowsInit); | |
78 // Get a C-style string | |
79 const char* str = env->GetStringUTFChars((jstring) result, NULL); | |
80 printf("%s\n", str); | |
81 // Clean up | |
82 env->ReleaseStringUTFChars(jstr, str); | |
83 #endif | |
84 return env->NewStringUTF("Hello from JNI!"); | 77 return env->NewStringUTF("Hello from JNI!"); |
85 } | 78 } |
86 | 79 |
87 /* Implement these to get and set the Box* pointer on the widget handle */ | 80 /* Implement these to get and set the Box* pointer on the widget handle */ |
88 void *_dw_window_pointer_get(HWND handle) | 81 void *_dw_window_pointer_get(HWND handle) |
90 return NULL; | 83 return NULL; |
91 } | 84 } |
92 | 85 |
93 void _dw_window_pointer_set(HWND handle, Box *box) | 86 void _dw_window_pointer_set(HWND handle, Box *box) |
94 { | 87 { |
95 } | |
96 | |
97 /* This function calculates how much space the widgets and boxes require | |
98 * and does expansion as necessary. | |
99 */ | |
100 static void _dw_resize_box(Box *thisbox, int *depth, int x, int y, int pass) | |
101 { | |
102 /* Current item position */ | |
103 int z, currentx = thisbox->pad, currenty = thisbox->pad; | |
104 /* Used x, y and padding maximum values... | |
105 * These will be used to find the widest or | |
106 * tallest items in a box. | |
107 */ | |
108 int uymax = 0, uxmax = 0; | |
109 int upymax = 0, upxmax = 0; | |
110 | |
111 /* Reset the box sizes */ | |
112 thisbox->minwidth = thisbox->minheight = thisbox->usedpadx = thisbox->usedpady = thisbox->pad * 2; | |
113 | |
114 #if 0 | |
115 /* If there are containers which have built-in padding like | |
116 * groupboxes.. calculate the padding size and add it to the layout. | |
117 */ | |
118 if(thisbox->grouphwnd) | |
119 { | |
120 char *text = dw_window_get_text(thisbox->grouphwnd); | |
121 | |
122 thisbox->grouppady = 0; | |
123 | |
124 if(text) | |
125 { | |
126 dw_font_text_extents_get(thisbox->grouphwnd, 0, text, NULL, &thisbox->grouppady); | |
127 dw_free(text); | |
128 } | |
129 | |
130 if(thisbox->grouppady) | |
131 thisbox->grouppady += 3; | |
132 else | |
133 thisbox->grouppady = 6; | |
134 | |
135 thisbox->grouppadx = 6; | |
136 | |
137 thisbox->minwidth += thisbox->grouppadx; | |
138 thisbox->usedpadx += thisbox->grouppadx; | |
139 thisbox->minheight += thisbox->grouppady; | |
140 thisbox->usedpady += thisbox->grouppady; | |
141 } | |
142 #endif | |
143 | |
144 /* Count up all the space for all items in the box */ | |
145 for(z=0;z<thisbox->count;z++) | |
146 { | |
147 int itempad, itemwidth, itemheight; | |
148 | |
149 if(thisbox->items[z].type == TYPEBOX) | |
150 { | |
151 Box *tmp = (Box *)_dw_window_pointer_get(thisbox->items[z].hwnd); | |
152 | |
153 if(tmp) | |
154 { | |
155 /* On the first pass calculate the box contents */ | |
156 if(pass == 1) | |
157 { | |
158 (*depth)++; | |
159 | |
160 /* Save the newly calculated values on the box */ | |
161 _dw_resize_box(tmp, depth, x, y, pass); | |
162 | |
163 /* Duplicate the values in the item list for use below */ | |
164 thisbox->items[z].width = tmp->minwidth; | |
165 thisbox->items[z].height = tmp->minheight; | |
166 | |
167 (*depth)--; | |
168 } | |
169 } | |
170 } | |
171 | |
172 /* Precalculate these values, since they will | |
173 * be used used repeatedly in the next section. | |
174 */ | |
175 itempad = thisbox->items[z].pad * 2; | |
176 itemwidth = thisbox->items[z].width + itempad; | |
177 itemheight = thisbox->items[z].height + itempad; | |
178 | |
179 /* Calculate the totals and maximums */ | |
180 if(thisbox->type == DW_VERT) | |
181 { | |
182 if(itemwidth > uxmax) | |
183 uxmax = itemwidth; | |
184 | |
185 if(thisbox->items[z].hsize != SIZEEXPAND) | |
186 { | |
187 if(itemwidth > upxmax) | |
188 upxmax = itemwidth; | |
189 } | |
190 else | |
191 { | |
192 if(itempad > upxmax) | |
193 upxmax = itempad; | |
194 } | |
195 thisbox->minheight += itemheight; | |
196 if(thisbox->items[z].vsize != SIZEEXPAND) | |
197 thisbox->usedpady += itemheight; | |
198 else | |
199 thisbox->usedpady += itempad; | |
200 } | |
201 else | |
202 { | |
203 if(itemheight > uymax) | |
204 uymax = itemheight; | |
205 if(thisbox->items[z].vsize != SIZEEXPAND) | |
206 { | |
207 if(itemheight > upymax) | |
208 upymax = itemheight; | |
209 } | |
210 else | |
211 { | |
212 if(itempad > upymax) | |
213 upymax = itempad; | |
214 } | |
215 thisbox->minwidth += itemwidth; | |
216 if(thisbox->items[z].hsize != SIZEEXPAND) | |
217 thisbox->usedpadx += itemwidth; | |
218 else | |
219 thisbox->usedpadx += itempad; | |
220 } | |
221 } | |
222 | |
223 /* Add the maximums which were calculated in the previous loop */ | |
224 thisbox->minwidth += uxmax; | |
225 thisbox->minheight += uymax; | |
226 thisbox->usedpadx += upxmax; | |
227 thisbox->usedpady += upymax; | |
228 | |
229 /* Move the groupbox start past the group border */ | |
230 if(thisbox->grouphwnd) | |
231 { | |
232 currentx += 3; | |
233 currenty += thisbox->grouppady - 3; | |
234 } | |
235 | |
236 /* The second pass is for actual placement. */ | |
237 if(pass > 1) | |
238 { | |
239 for(z=0;z<(thisbox->count);z++) | |
240 { | |
241 int height = thisbox->items[z].height; | |
242 int width = thisbox->items[z].width; | |
243 int itempad = thisbox->items[z].pad * 2; | |
244 int thispad = thisbox->pad * 2; | |
245 | |
246 /* Calculate the new sizes */ | |
247 if(thisbox->items[z].hsize == SIZEEXPAND) | |
248 { | |
249 if(thisbox->type == DW_HORZ) | |
250 { | |
251 int expandablex = thisbox->minwidth - thisbox->usedpadx; | |
252 | |
253 if(expandablex) | |
254 width = (int)(((float)width / (float)expandablex) * (float)(x - thisbox->usedpadx)); | |
255 } | |
256 else | |
257 width = x - (itempad + thispad + thisbox->grouppadx); | |
258 } | |
259 if(thisbox->items[z].vsize == SIZEEXPAND) | |
260 { | |
261 if(thisbox->type == DW_VERT) | |
262 { | |
263 int expandabley = thisbox->minheight - thisbox->usedpady; | |
264 | |
265 if(expandabley) | |
266 height = (int)(((float)height / (float)expandabley) * (float)(y - thisbox->usedpady)); | |
267 } | |
268 else | |
269 height = y - (itempad + thispad + thisbox->grouppady); | |
270 } | |
271 | |
272 /* If the calculated size is valid... */ | |
273 if(width > 0 && height > 0) | |
274 { | |
275 int pad = thisbox->items[z].pad; | |
276 #if 0 | |
277 HWND handle = thisbox->items[z].hwnd; | |
278 | |
279 /* Here you put your platform specific placement widget placement code */ | |
280 PlaceWidget(handle, currentx + pad, currenty + pad, width, height); | |
281 | |
282 /* If any special handling needs to be done... like diving into | |
283 * controls that have sub-layouts... like notebooks or splitbars... | |
284 * do that here. Figure out the sub-layout size and call _dw_do_resize(). | |
285 */ | |
286 #endif | |
287 | |
288 /* Advance the current position in the box */ | |
289 if(thisbox->type == DW_HORZ) | |
290 currentx += width + (pad * 2); | |
291 if(thisbox->type == DW_VERT) | |
292 currenty += height + (pad * 2); | |
293 } | |
294 } | |
295 } | |
296 } | |
297 | |
298 /* This is a convenience function used in the window's resize event | |
299 * to relayout the controls in the window. | |
300 */ | |
301 void _dw_do_resize(Box *thisbox, int x, int y) | |
302 { | |
303 if(x != 0 && y != 0) | |
304 { | |
305 if(thisbox) | |
306 { | |
307 int depth = 0; | |
308 | |
309 /* Calculate space requirements */ | |
310 _dw_resize_box(thisbox, &depth, x, y, 1); | |
311 | |
312 /* Finally place all the boxes and controls */ | |
313 _dw_resize_box(thisbox, &depth, x, y, 2); | |
314 } | |
315 } | |
316 } | 88 } |
317 | 89 |
318 /* | 90 /* |
319 * Runs a message loop for Dynamic Windows. | 91 * Runs a message loop for Dynamic Windows. |
320 */ | 92 */ |
588 * Returns: | 360 * Returns: |
589 * A handle to a box or NULL on failure. | 361 * A handle to a box or NULL on failure. |
590 */ | 362 */ |
591 HWND API dw_box_new(int type, int pad) | 363 HWND API dw_box_new(int type, int pad) |
592 { | 364 { |
365 JNIEnv *env; | |
366 | |
367 if((env = (JNIEnv *)pthread_getspecific(_dw_env_key))) | |
368 { | |
369 // First get the class that contains the method you need to call | |
370 jclass clazz = env->FindClass("org/dbsoft/dwindows/dwtest/DWindows"); | |
371 // Get the method that you want to call | |
372 jmethodID boxNew = env->GetMethodID(clazz, "boxNew", "(Ljava/lang/String;)V"); | |
373 // Call the method on the object | |
374 jobject result = env->CallObjectMethod(_dw_obj, boxNew, type, pad); | |
375 return result; | |
376 } | |
593 return 0; | 377 return 0; |
594 } | 378 } |
595 | 379 |
596 /* | 380 /* |
597 * Create a new Group Box to be packed. | 381 * Create a new Group Box to be packed. |
647 } | 431 } |
648 | 432 |
649 /* Internal box packing function called by the other 3 functions */ | 433 /* Internal box packing function called by the other 3 functions */ |
650 void _dw_box_pack(HWND box, HWND item, int index, int width, int height, int hsize, int vsize, int pad, char *funcname) | 434 void _dw_box_pack(HWND box, HWND item, int index, int width, int height, int hsize, int vsize, int pad, char *funcname) |
651 { | 435 { |
652 Box *thisbox; | 436 JNIEnv *env; |
653 int z, x = 0; | 437 |
654 Item *tmpitem, *thisitem; | 438 if((env = (JNIEnv *)pthread_getspecific(_dw_env_key))) |
655 | |
656 /* Sanity checks */ | |
657 if(!box || box == item) | |
658 return; | |
659 | |
660 thisbox = (Box *)_dw_window_pointer_get(box); | |
661 thisitem = thisbox->items; | |
662 | |
663 /* Do some sanity bounds checking */ | |
664 if(index < 0) | |
665 index = 0; | |
666 if(index > thisbox->count) | |
667 index = thisbox->count; | |
668 | |
669 /* Duplicate the existing data */ | |
670 tmpitem = (Item *)malloc(sizeof(Item)*(thisbox->count+1)); | |
671 | |
672 for(z=0;z<thisbox->count;z++) | |
673 { | 439 { |
674 if(z == index) | 440 // First get the class that contains the method you need to call |
675 x++; | 441 jclass clazz = env->FindClass("org/dbsoft/dwindows/dwtest/DWindows"); |
676 tmpitem[x] = thisitem[z]; | 442 // Get the method that you want to call |
677 x++; | 443 jmethodID boxPack = env->GetMethodID(clazz, "boxPack", "(Ljava/lang/String;)V"); |
444 // Call the method on the object | |
445 env->CallVoidMethod(_dw_obj, boxPack, box, item, index, width, height, hsize, vsize, pad); | |
678 } | 446 } |
679 | |
680 /* Sanity checks */ | |
681 if(vsize && !height) | |
682 height = 1; | |
683 if(hsize && !width) | |
684 width = 1; | |
685 | |
686 /* Fill in the item data appropriately */ | |
687 if(0 /* Test to see if "item" is a box */) | |
688 tmpitem[index].type = TYPEBOX; | |
689 else | |
690 tmpitem[index].type = TYPEITEM; | |
691 | |
692 tmpitem[index].hwnd = item; | |
693 tmpitem[index].origwidth = tmpitem[index].width = width; | |
694 tmpitem[index].origheight = tmpitem[index].height = height; | |
695 tmpitem[index].pad = pad; | |
696 if(hsize) | |
697 tmpitem[index].hsize = SIZEEXPAND; | |
698 else | |
699 tmpitem[index].hsize = SIZESTATIC; | |
700 | |
701 if(vsize) | |
702 tmpitem[index].vsize = SIZEEXPAND; | |
703 else | |
704 tmpitem[index].vsize = SIZESTATIC; | |
705 | |
706 thisbox->items = tmpitem; | |
707 | |
708 /* Update the item count */ | |
709 thisbox->count++; | |
710 | |
711 /* Add the item to the box */ | |
712 #if 0 | |
713 /* Platform specific code to add item to box */ | |
714 BoxAdd(box, item); | |
715 #endif | |
716 | |
717 /* Free the old data */ | |
718 if(thisbox->count) | |
719 free(thisitem); | |
720 } | 447 } |
721 | 448 |
722 /* | 449 /* |
723 * Remove windows (widgets) from the box they are packed into. | 450 * Remove windows (widgets) from the box they are packed into. |
724 * Parameters: | 451 * Parameters: |
2680 * Returns: | 2407 * Returns: |
2681 * Handle to the created window or NULL on error. | 2408 * Handle to the created window or NULL on error. |
2682 */ | 2409 */ |
2683 HWND API dw_window_new(HWND hwndOwner, const char *title, ULONG flStyle) | 2410 HWND API dw_window_new(HWND hwndOwner, const char *title, ULONG flStyle) |
2684 { | 2411 { |
2412 JNIEnv *env; | |
2413 | |
2414 if((env = (JNIEnv *)pthread_getspecific(_dw_env_key))) | |
2415 { | |
2416 // Construct a String | |
2417 jstring jstr = env->NewStringUTF(title); | |
2418 // First get the class that contains the method you need to call | |
2419 jclass clazz = env->FindClass("org/dbsoft/dwindows/dwtest/DWindows"); | |
2420 // Get the method that you want to call | |
2421 jmethodID windowNew = env->GetMethodID(clazz, "windowNew", "(Ljava/lang/String;)V"); | |
2422 // Call the method on the object | |
2423 jobject result = env->CallObjectMethod(_dw_obj, windowNew, jstr); | |
2424 return result; | |
2425 } | |
2685 return 0; | 2426 return 0; |
2686 } | 2427 } |
2687 | 2428 |
2688 /* | 2429 /* |
2689 * Call a function from the window (widget)'s context (typically the message loop thread). | 2430 * Call a function from the window (widget)'s context (typically the message loop thread). |
3898 * However it is exported so language bindings can call it when | 3639 * However it is exported so language bindings can call it when |
3899 * they create threads that require access to Dynamic Windows. | 3640 * they create threads that require access to Dynamic Windows. |
3900 */ | 3641 */ |
3901 void API _dw_init_thread(void) | 3642 void API _dw_init_thread(void) |
3902 { | 3643 { |
3903 HEV event = dw_event_new(); | 3644 JNIEnv *env; |
3904 | 3645 |
3905 pthread_setspecific(_dw_event_key, event); | 3646 _dw_jvm->AttachCurrentThread(&env, NULL); |
3647 pthread_setspecific(_dw_env_key, env); | |
3906 } | 3648 } |
3907 | 3649 |
3908 /* | 3650 /* |
3909 * Generally an internal function called from a terminating | 3651 * Generally an internal function called from a terminating |
3910 * thread to cleanup the Dynamic Windows environment for the thread. | 3652 * thread to cleanup the Dynamic Windows environment for the thread. |
3911 * However it is exported so language bindings can call it when | 3653 * However it is exported so language bindings can call it when |
3912 * they exit threads that require access to Dynamic Windows. | 3654 * they exit threads that require access to Dynamic Windows. |
3913 */ | 3655 */ |
3914 void API _dw_deinit_thread(void) | 3656 void API _dw_deinit_thread(void) |
3915 { | 3657 { |
3916 HEV event; | 3658 _dw_jvm->DetachCurrentThread(); |
3917 | |
3918 if((event = (HEV)pthread_getspecific(_dw_event_key))) | |
3919 dw_event_close(&event); | |
3920 } | 3659 } |
3921 | 3660 |
3922 /* | 3661 /* |
3923 * Setup thread independent color sets. | 3662 * Setup thread independent color sets. |
3924 */ | 3663 */ |