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 */