# HG changeset patch # User bsmith@81767d24-ef19-dc11-ae90-00e081727c95 # Date 1620686092 0 # Node ID b9923432cb1fef2b5a79f2255d805bdb98120782 # Parent 060fdb2d807d45c2c164cb0cc4ae09ecb2d99f17 Android: Implement View based render widget and icon support with Drawable. Return nullptr instead of 0 on any functions using jobject. diff -r 060fdb2d807d -r b9923432cb1f android/DWindows.kt --- a/android/DWindows.kt Mon May 10 20:06:50 2021 +0000 +++ b/android/DWindows.kt Mon May 10 22:34:52 2021 +0000 @@ -13,6 +13,9 @@ import android.content.res.Configuration import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.media.AudioManager import android.media.ToneGenerator @@ -41,13 +44,13 @@ import androidx.collection.SimpleArrayMap import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.content.res.ResourcesCompat import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout.OnTabSelectedListener import com.google.android.material.tabs.TabLayoutMediator import java.io.File -import java.io.FileFilter import java.io.FileInputStream import java.io.FileNotFoundException import java.util.* @@ -239,6 +242,32 @@ ) } +class DWRender(context: Context) : View(context) { + var cachedCanvas: Canvas? = null + + override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) { + super.onSizeChanged(width, height, oldWidth, oldHeight) + // Send DW_SIGNAL_CONFIGURE + eventHandlerInt(1, width, height, 0, 0) + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + cachedCanvas = canvas + // Send DW_SIGNAL_EXPOSE + eventHandlerInt(7, 0, 0, this.width, this.height) + cachedCanvas = null + } + + external fun eventHandlerInt( + message: Int, + inta: Int, + intb: Int, + intc: Int, + intd: Int + ) +} + class DWFileChooser(private val activity: Activity) { private val list: ListView = ListView(activity) private val dialog: Dialog = Dialog(activity) @@ -1737,6 +1766,26 @@ } } + fun iconNew(filename: String?, data: ByteArray?, length: Int, resID: Int): Drawable? + { + var icon: Drawable? = null + + waitOnUiThread { + if(resID != 0) { + icon = ResourcesCompat.getDrawable(resources, resID, null); + } else if(filename != null) { + // Try to load the image, and protect against exceptions + try { + icon = Drawable.createFromPath(filename) + } catch (e: FileNotFoundException) { + } + } else if(data != null) { + icon = BitmapDrawable(resources, BitmapFactory.decodeByteArray(data, 0, length)) + } + } + return icon + } + fun pixmapNew(width: Int, height: Int, filename: String?, data: ByteArray?, length: Int, resID: Int): Bitmap? { var pixmap: Bitmap? = null @@ -1770,6 +1819,25 @@ return dimensions } + fun renderNew(cid: Int): DWRender? + { + var render: DWRender? = null + + waitOnUiThread { + var dataArrayMap = SimpleArrayMap() + + render = DWRender(this) + render!!.tag = dataArrayMap + render!!.id = cid + } + return render + } + + fun renderRedraw(render: DWRender) + { + render.invalidate() + } + fun timerConnect(interval: Long, sigfunc: Long, data: Long): Timer { // creating timer task, timer @@ -1886,6 +1954,14 @@ return retval } + fun isUIThread(): Boolean + { + if(Looper.getMainLooper() == Looper.myLooper()) { + return true + } + return false + } + fun mainSleep(milliseconds: Int) { // If we are on the main UI thread... add an idle handler diff -r 060fdb2d807d -r b9923432cb1f android/dw.cpp --- a/android/dw.cpp Mon May 10 20:06:50 2021 +0000 +++ b/android/dw.cpp Mon May 10 22:34:52 2021 +0000 @@ -471,6 +471,16 @@ _dw_event_handler(obj, params, message); } +JNIEXPORT void JNICALL +Java_org_dbsoft_dwindows_DWRender_eventHandlerInt(JNIEnv* env, jobject obj, jint message, + jint inta, jint intb, jint intc, jint intd) { + void *params[8] = { nullptr, nullptr, nullptr, + DW_INT_TO_POINTER(inta), DW_INT_TO_POINTER(intb), + DW_INT_TO_POINTER(intc), DW_INT_TO_POINTER(intd), nullptr }; + + _dw_event_handler(obj, params, message); +} + /* Handler for Timer events */ JNIEXPORT jint JNICALL Java_org_dbsoft_dwindows_DWindows_eventHandlerTimer(JNIEnv* env, jobject obj, jlong sigfunc, jlong data) { @@ -925,7 +935,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, boxNew, type, pad)); return result; } - return 0; + return nullptr; } /* @@ -966,7 +976,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, scrollBoxNew, type, pad)); return result; } - return 0; + return nullptr; } /* @@ -1046,7 +1056,7 @@ HWND API dw_box_unpack_at_index(HWND box, int index) { JNIEnv *env; - HWND retval = 0; + HWND retval = nullptr; if(box && (env = (JNIEnv *)pthread_getspecific(_dw_env_key))) { // First get the class that contains the method you need to call @@ -1137,7 +1147,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, buttonNew, jstr, (int)cid)); return result; } - return 0; + return nullptr; } HWND _dw_entryfield_new(const char *text, ULONG cid, int password) @@ -1157,7 +1167,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, entryfieldNew, jstr, (int)cid, password)); return result; } - return 0; + return nullptr; } /* * Create a new Entryfield window (widget) to be packed. @@ -1232,7 +1242,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, bitmapButtonNew, jstr, (int)resid)); return result; } - return 0; + return nullptr; } /* @@ -1264,7 +1274,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, bitmapButtonNewFromFile, jstr, (int)cid, path)); return result; } - return 0; + return nullptr; } /* @@ -1300,7 +1310,7 @@ //env->ReleaseByteArrayElements(bytearray, (jbyte *) data, 0); return result; } - return 0; + return nullptr; } /* @@ -1328,7 +1338,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, spinButtonNew, jstr, (int)cid)); return result; } - return 0; + return nullptr; } /* @@ -1426,7 +1436,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, radioButtonNew, jstr, (int)cid)); return result; } - return 0; + return nullptr; } /* @@ -1452,7 +1462,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, sliderNew, vertical, increments, (jint)cid)); return result; } - return 0; + return nullptr; } /* @@ -1571,7 +1581,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, percentNew, (jint)cid)); return result; } - return 0; + return nullptr; } /* @@ -1621,7 +1631,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, checkboxNew, jstr, (int)cid)); return result; } - return 0; + return nullptr; } /* @@ -1693,7 +1703,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, listBoxNew, (int)cid, multi)); return result; } - return 0; + return nullptr; } /* @@ -2006,7 +2016,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, comboBoxNew, jstr, (int)cid)); return result; } - return 0; + return nullptr; } /* @@ -2031,7 +2041,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, mleNew, (int)cid)); return result; } - return 0; + return nullptr; } /* @@ -2304,7 +2314,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, textNew, jstr, (int)cid, status)); return result; } - return 0; + return nullptr; } /* @@ -2342,7 +2352,20 @@ */ HWND API dw_render_new(unsigned long cid) { - return 0; + JNIEnv *env; + + if((env = (JNIEnv *)pthread_getspecific(_dw_env_key))) + { + // First get the class that contains the method you need to call + jclass clazz = _dw_find_class(env, DW_CLASS_NAME); + // Get the method that you want to call + jmethodID renderNew = env->GetMethodID(clazz, "renderNew", + "(I)Lorg/dbsoft/dwindows/DWRender;"); + // Call the method on the object + jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, renderNew, (int)cid)); + return result; + } + return nullptr; } /* @@ -2352,6 +2375,18 @@ */ void API dw_render_redraw(HWND handle) { + JNIEnv *env; + + if(handle && (env = (JNIEnv *)pthread_getspecific(_dw_env_key))) + { + // First get the class that contains the method you need to call + jclass clazz = _dw_find_class(env, DW_CLASS_NAME); + // Get the method that you want to call + jmethodID renderRedraw = env->GetMethodID(clazz, "renderRedraw", + "(Lorg/dbsoft/dwindows/DWRender;)V"); + // Call the method on the object + env->CallVoidMethod(_dw_obj, renderRedraw, handle); + } } /* Sets the current foreground drawing color. @@ -2487,7 +2522,7 @@ */ HWND API dw_tree_new(ULONG cid) { - return 0; + return nullptr; } /* @@ -2520,7 +2555,7 @@ */ HTREEITEM API dw_tree_insert(HWND handle, const char *title, HICN icon, HTREEITEM parent, void *itemdata) { - return 0; + return nullptr; } /* @@ -2546,7 +2581,7 @@ */ HTREEITEM API dw_tree_get_parent(HWND handle, HTREEITEM item) { - return 0; + return nullptr; } /* @@ -2644,7 +2679,7 @@ */ HWND API dw_container_new(ULONG cid, int multi) { - return 0; + return nullptr; } /* @@ -3016,6 +3051,39 @@ { } +HICN _dw_icon_load(const char *filename, const char *data, int len, int resid) +{ + JNIEnv *env; + + if((env = (JNIEnv *)pthread_getspecific(_dw_env_key))) + { + // Construct a string + jstring file = nullptr; + if(filename) + file = env->NewStringUTF(filename); + // Construct a byte array + jbyteArray bytearray = nullptr; + if(data && len > 0) + { + bytearray = env->NewByteArray(len); + env->SetByteArrayRegion(bytearray, 0, len, reinterpret_cast(data)); + } + // First get the class that contains the method you need to call + jclass clazz = _dw_find_class(env, DW_CLASS_NAME); + // Get the method that you want to call + jmethodID iconNew = env->GetMethodID(clazz, "iconNew", + "(Ljava/lang/String;[BII)Landroid/graphics/drawable/Drawable;"); + // Call the method on the object + jobject result = env->NewGlobalRef(env->CallObjectMethod(_dw_obj, iconNew, + file, bytearray, len, resid)); + // Clean up after the array now that we are finished + //if(bytearray) + //env->ReleaseByteArrayElements(bytearray, (jbyte *) data, 0); + return result; + } + return nullptr; +} + /* * Obtains an icon from a module (or header in GTK). * Parameters: @@ -3028,7 +3096,7 @@ */ HICN API dw_icon_load(unsigned long module, unsigned long resid) { - return 0; + return _dw_icon_load(NULL, NULL, 0, resid); } /* @@ -3042,7 +3110,7 @@ */ HICN API dw_icon_load_from_file(const char *filename) { - return 0; + return _dw_icon_load(filename, NULL, 0, 0); } /* @@ -3055,7 +3123,7 @@ */ HICN API dw_icon_load_from_data(const char *data, int len) { - return 0; + return _dw_icon_load(NULL, data, len, 0); } /* @@ -3065,6 +3133,15 @@ */ void API dw_icon_free(HICN handle) { + if(handle) + { + JNIEnv *env; + + if(handle && (env = (JNIEnv *)pthread_getspecific(_dw_env_key))) + { + env->DeleteGlobalRef(handle); + } + } } /* @@ -3425,7 +3502,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, calendarNew, (int)cid)); return result; } - return 0; + return nullptr; } /* @@ -3625,7 +3702,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, htmlNew, (int)cid)); return result; } - return 0; + return nullptr; } /* @@ -3657,7 +3734,7 @@ */ HMENUI API dw_menu_new(ULONG cid) { - return 0; + return nullptr; } /* @@ -3669,7 +3746,7 @@ */ HMENUI API dw_menubar_new(HWND location) { - return 0; + return nullptr; } /* @@ -3721,7 +3798,7 @@ */ HWND API dw_menu_append_item(HMENUI menux, const char *title, ULONG itemid, ULONG flags, int end, int check, HMENUI submenux) { - return 0; + return nullptr; } /* @@ -3771,7 +3848,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, notebookNew, (int)cid, top)); return result; } - return 0; + return nullptr; } /* @@ -3953,7 +4030,7 @@ jobject result = env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, windowNew, jstr, (int)flStyle)); return result; } - return 0; + return nullptr; } /* @@ -4363,7 +4440,7 @@ HWND API dw_window_from_id(HWND handle, int id) { JNIEnv *env; - HWND retval = 0; + HWND retval = nullptr; if((env = (JNIEnv *)pthread_getspecific(_dw_env_key))) { @@ -4941,6 +5018,24 @@ } } +/* Check if we are on the UI thread */ +int _dw_is_ui_thread(void) +{ + JNIEnv *env; + + if((env = (JNIEnv *)pthread_getspecific(_dw_env_key))) + { + // First get the class that contains the method you need to call + jclass clazz = _dw_find_class(env, DW_CLASS_NAME); + // Get the method that you want to call + jmethodID isUIThread = env->GetMethodID(clazz, "isUIThread", + "()Z"); + // Call the method on the object + return env->CallBooleanMethod(_dw_obj, isUIThread); + } + return FALSE; +} + /* * Tries to gain access to the semaphore, if it can't it blocks. * Parameters: @@ -4953,26 +5048,16 @@ * will deadlock... so try to acquire the lock and continue * processing messages in between tries. */ -#if 0 /* TODO: Not sure how to do this on Android yet */ - if(_dw_thread == pthread_self()) + if(_dw_is_ui_thread()) { while(pthread_mutex_trylock(mutex) != 0) { /* Process any pending events */ - if(g_main_context_pending(nullptr)) - { - do - { - g_main_context_iteration(nullptr, FALSE); - } - while(g_main_context_pending(nullptr)); - } - else - sched_yield(); + dw_main_iteration(); + sched_yield(); } } else -#endif { pthread_mutex_lock(mutex); } diff -r 060fdb2d807d -r b9923432cb1f dw.h --- a/dw.h Mon May 10 20:06:50 2021 +0000 +++ b/dw.h Mon May 10 22:34:52 2021 +0000 @@ -771,7 +771,7 @@ typedef void *HSHM; typedef void *HTREEITEM; typedef HWND HMENUI; -typedef unsigned long HICN; +typedef jobject HICN; typedef struct _window_data { UserData *root;