summaryrefslogtreecommitdiff
path: root/src/drivers/Android/Fl_Android_Screen_Driver.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/Android/Fl_Android_Screen_Driver.cxx')
-rw-r--r--src/drivers/Android/Fl_Android_Screen_Driver.cxx1106
1 files changed, 1106 insertions, 0 deletions
diff --git a/src/drivers/Android/Fl_Android_Screen_Driver.cxx b/src/drivers/Android/Fl_Android_Screen_Driver.cxx
new file mode 100644
index 000000000..a8a8d038d
--- /dev/null
+++ b/src/drivers/Android/Fl_Android_Screen_Driver.cxx
@@ -0,0 +1,1106 @@
+//
+// "$Id: Fl_Android_Screen_Driver.cxx 12655 2018-02-09 14:39:42Z AlbrechtS $"
+//
+// Windows screen interface for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2018 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// http://www.fltk.org/COPYING.php
+//
+// Please report all bugs and problems on the following page:
+//
+// http://www.fltk.org/str.php
+//
+
+
+#include "../../config_lib.h"
+#include "Fl_Android_Screen_Driver.H"
+#include "./Fl_Font.H"
+#include <FL/Fl.H>
+#include <FL/platform.H>
+#include <FL/Fl_Graphics_Driver.H>
+#include <FL/Fl_RGB_Image.H>
+#include <FL/fl_ask.H>
+#include <stdio.h>
+
+#if 0
+
+// these are set by Fl::args() and override any system colors: from Fl_get_system_colors.cxx
+extern const char *fl_fg;
+extern const char *fl_bg;
+extern const char *fl_bg2;
+// end of extern additions workaround
+
+
+#if !defined(HMONITOR_DECLARED) && (_WIN32_WINNT < 0x0500)
+# define COMPILE_MULTIMON_STUBS
+# include <multimon.h>
+#endif // !HMONITOR_DECLARED && _WIN32_WINNT < 0x0500
+
+#endif
+
+/*
+ Creates a driver that manages all screen and display related calls.
+
+ This function must be implemented once for every platform.
+ */
+Fl_Screen_Driver *Fl_Screen_Driver::newScreenDriver()
+{
+ return new Fl_Android_Screen_Driver();
+}
+
+#if 0
+
+int Fl_WinAPI_Screen_Driver::visual(int flags)
+{
+ fl_GetDC(0);
+ if (flags & FL_DOUBLE) return 0;
+ HDC gc = (HDC)Fl_Graphics_Driver::default_driver().gc();
+ if (!(flags & FL_INDEX) &&
+ GetDeviceCaps(gc,BITSPIXEL) <= 8) return 0;
+ if ((flags & FL_RGB8) && GetDeviceCaps(gc,BITSPIXEL)<24) return 0;
+ return 1;
+}
+
+
+// We go the much more difficult route of individually picking some multi-screen
+// functions from the USER32.DLL . If these functions are not available, we
+// will gracefully fall back to single monitor support.
+//
+// If we were to insist on the existence of "EnumDisplayMonitors" and
+// "GetMonitorInfoA", it would be impossible to use FLTK on Windows 2000
+// before SP2 or earlier.
+
+// BOOL EnumDisplayMonitors(HDC, LPCRECT, MONITORENUMPROC, LPARAM)
+typedef BOOL(WINAPI* fl_edm_func)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
+// BOOL GetMonitorInfo(HMONITOR, LPMONITORINFO)
+typedef BOOL(WINAPI* fl_gmi_func)(HMONITOR, LPMONITORINFO);
+
+static fl_gmi_func fl_gmi = NULL; // used to get a proc pointer for GetMonitorInfoA
+
+
+BOOL Fl_WinAPI_Screen_Driver::screen_cb(HMONITOR mon, HDC hdc, LPRECT r, LPARAM d)
+{
+ Fl_WinAPI_Screen_Driver *drv = (Fl_WinAPI_Screen_Driver*)d;
+ return drv->screen_cb(mon, hdc, r);
+}
+
+
+BOOL Fl_WinAPI_Screen_Driver::screen_cb(HMONITOR mon, HDC, LPRECT r)
+{
+ if (num_screens >= MAX_SCREENS) return TRUE;
+
+ MONITORINFOEX mi;
+ mi.cbSize = sizeof(mi);
+
+ // GetMonitorInfo(mon, &mi);
+ // (but we use our self-acquired function pointer instead)
+ if (fl_gmi(mon, &mi)) {
+ screens[num_screens] = mi.rcMonitor;
+ // If we also want to record the work area, we would also store mi.rcWork at this point
+ work_area[num_screens] = mi.rcWork;
+//extern FILE*LOG;fprintf(LOG,"screen_cb ns=%d\n",num_screens);fflush(LOG);
+ /*fl_alert("screen %d %d,%d,%d,%d work %d,%d,%d,%d",num_screens,
+ screens[num_screens].left,screens[num_screens].right,screens[num_screens].top,screens[num_screens].bottom,
+ work_area[num_screens].left,work_area[num_screens].right,work_area[num_screens].top,work_area[num_screens].bottom);
+ */
+ // find the pixel size
+ if (mi.cbSize == sizeof(mi)) {
+ HDC screen = CreateDC(mi.szDevice, NULL, NULL, NULL);
+ if (screen) {
+ dpi[num_screens][0] = (float)GetDeviceCaps(screen, LOGPIXELSX);
+ dpi[num_screens][1] = (float)GetDeviceCaps(screen, LOGPIXELSY);
+ }
+ DeleteDC(screen);
+ }
+
+ num_screens++;
+ }
+ return TRUE;
+}
+
+
+void Fl_WinAPI_Screen_Driver::init()
+{
+ open_display();
+ // Since not all versions of Windows include multiple monitor support,
+ // we do a run-time check for the required functions...
+ HMODULE hMod = GetModuleHandle("USER32.DLL");
+
+ if (hMod) {
+ // check that EnumDisplayMonitors is available
+ fl_edm_func fl_edm = (fl_edm_func)GetProcAddress(hMod, "EnumDisplayMonitors");
+
+ if (fl_edm) {
+ // we have EnumDisplayMonitors - do we also have GetMonitorInfoA ?
+ fl_gmi = (fl_gmi_func)GetProcAddress(hMod, "GetMonitorInfoA");
+ if (fl_gmi) {
+ // We have GetMonitorInfoA, enumerate all the screens...
+ // EnumDisplayMonitors(0,0,screen_cb,0);
+ // (but we use our self-acquired function pointer instead)
+ // NOTE: num_screens is incremented in screen_cb so we must first reset it here...
+ num_screens = 0;
+ fl_edm(0, 0, screen_cb, (LPARAM)this);
+ return;
+ }
+ }
+ }
+
+ // If we get here, assume we have 1 monitor...
+ num_screens = 1;
+ screens[0].top = 0;
+ screens[0].left = 0;
+ screens[0].right = GetSystemMetrics(SM_CXSCREEN);
+ screens[0].bottom = GetSystemMetrics(SM_CYSCREEN);
+ work_area[0] = screens[0];
+ scale_of_screen[0] = 1;
+}
+
+
+float Fl_WinAPI_Screen_Driver::desktop_scale_factor() {
+ return 0; //indicates each screen has already been assigned its scale factor value
+}
+
+
+void Fl_WinAPI_Screen_Driver::screen_work_area(int &X, int &Y, int &W, int &H, int n)
+{
+ if (num_screens < 0) init();
+ if (n < 0 || n >= num_screens) n = 0;
+ X = work_area[n].left/scale_of_screen[n];
+ Y = work_area[n].top/scale_of_screen[n];
+ W = (work_area[n].right - X)/scale_of_screen[n];
+ H = (work_area[n].bottom - Y)/scale_of_screen[n];
+}
+
+
+void Fl_WinAPI_Screen_Driver::screen_xywh(int &X, int &Y, int &W, int &H, int n)
+{
+ if (num_screens < 0) init();
+
+ if ((n < 0) || (n >= num_screens))
+ n = 0;
+
+ if (num_screens > 0) {
+ X = screens[n].left/scale_of_screen[n];
+ Y = screens[n].top/scale_of_screen[n];
+ W = (screens[n].right - screens[n].left)/scale_of_screen[n];
+ H = (screens[n].bottom - screens[n].top)/scale_of_screen[n];
+ } else {
+ /* Fallback if something is broken... */
+ X = 0;
+ Y = 0;
+ W = GetSystemMetrics(SM_CXSCREEN);
+ H = GetSystemMetrics(SM_CYSCREEN);
+ }
+}
+
+
+void Fl_WinAPI_Screen_Driver::screen_dpi(float &h, float &v, int n)
+{
+ if (num_screens < 0) init();
+ h = v = 0.0f;
+ if (n >= 0 && n < num_screens) {
+ h = float(dpi[n][0]);
+ v = float(dpi[n][1]);
+ }
+}
+
+
+int Fl_WinAPI_Screen_Driver::x()
+{
+ RECT r;
+
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
+ return r.left;
+}
+
+
+int Fl_WinAPI_Screen_Driver::y()
+{
+ RECT r;
+
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
+ return r.top;
+}
+
+
+int Fl_WinAPI_Screen_Driver::h()
+{
+ RECT r;
+
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
+ return r.bottom - r.top;
+}
+
+
+int Fl_WinAPI_Screen_Driver::w()
+{
+ RECT r;
+
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
+ return r.right - r.left;
+}
+
+
+void Fl_WinAPI_Screen_Driver::beep(int type)
+{
+ switch (type) {
+ case FL_BEEP_QUESTION :
+ case FL_BEEP_PASSWORD :
+ MessageBeep(MB_ICONQUESTION);
+ break;
+ case FL_BEEP_MESSAGE :
+ MessageBeep(MB_ICONASTERISK);
+ break;
+ case FL_BEEP_NOTIFICATION :
+ MessageBeep(MB_ICONASTERISK);
+ break;
+ case FL_BEEP_ERROR :
+ MessageBeep(MB_ICONERROR);
+ break;
+ default :
+ MessageBeep(0xFFFFFFFF);
+ break;
+ }
+}
+
+
+void Fl_WinAPI_Screen_Driver::flush()
+{
+ GdiFlush();
+}
+
+
+extern void fl_fix_focus(); // in Fl.cxx
+
+// We have to keep track of whether we have captured the mouse, since
+// Windows shows little respect for this... Grep for fl_capture to
+// see where and how this is used.
+extern HWND fl_capture;
+
+
+void Fl_WinAPI_Screen_Driver::grab(Fl_Window* win)
+{
+ if (win) {
+ if (!Fl::grab_) {
+ SetActiveWindow(fl_capture = fl_xid(Fl::first_window()));
+ SetCapture(fl_capture);
+ }
+ Fl::grab_ = win;
+ } else {
+ if (Fl::grab_) {
+ fl_capture = 0;
+ ReleaseCapture();
+ Fl::grab_ = 0;
+ fl_fix_focus();
+ }
+ }
+}
+
+
+static void set_selection_color(uchar r, uchar g, uchar b)
+{
+ Fl::set_color(FL_SELECTION_COLOR,r,g,b);
+}
+
+
+static void getsyscolor(int what, const char* arg, void (*func)(uchar,uchar,uchar))
+{
+ if (arg) {
+ uchar r,g,b;
+ if (!fl_parse_color(arg, r,g,b))
+ Fl::error("Unknown color: %s", arg);
+ else
+ func(r,g,b);
+ } else {
+ DWORD x = GetSysColor(what);
+ func(uchar(x&255), uchar(x>>8), uchar(x>>16));
+ }
+}
+
+
+void Fl_WinAPI_Screen_Driver::get_system_colors()
+{
+ if (!bg2_set) getsyscolor(COLOR_WINDOW, fl_bg2,Fl::background2);
+ if (!fg_set) getsyscolor(COLOR_WINDOWTEXT, fl_fg, Fl::foreground);
+ if (!bg_set) getsyscolor(COLOR_BTNFACE, fl_bg, Fl::background);
+ getsyscolor(COLOR_HIGHLIGHT, 0, set_selection_color);
+}
+
+
+const char *Fl_WinAPI_Screen_Driver::get_system_scheme()
+{
+ return fl_getenv("FLTK_SCHEME");
+}
+
+
+// ---- timers
+
+
+struct Win32Timer
+{
+ UINT_PTR handle;
+ Fl_Timeout_Handler callback;
+ void *data;
+};
+static Win32Timer* win32_timers;
+static int win32_timer_alloc;
+static int win32_timer_used;
+static HWND s_TimerWnd;
+
+
+static void realloc_timers()
+{
+ if (win32_timer_alloc == 0) {
+ win32_timer_alloc = 8;
+ }
+ win32_timer_alloc *= 2;
+ Win32Timer* new_timers = new Win32Timer[win32_timer_alloc];
+ memset(new_timers, 0, sizeof(Win32Timer) * win32_timer_used);
+ memcpy(new_timers, win32_timers, sizeof(Win32Timer) * win32_timer_used);
+ Win32Timer* delete_me = win32_timers;
+ win32_timers = new_timers;
+ delete [] delete_me;
+}
+
+
+static void delete_timer(Win32Timer& t)
+{
+ KillTimer(s_TimerWnd, t.handle);
+ memset(&t, 0, sizeof(Win32Timer));
+}
+
+
+static LRESULT CALLBACK s_TimerProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_TIMER:
+ {
+ unsigned int id = (unsigned) (wParam - 1);
+ if (id < (unsigned int)win32_timer_used && win32_timers[id].handle) {
+ Fl_Timeout_Handler cb = win32_timers[id].callback;
+ void* data = win32_timers[id].data;
+ delete_timer(win32_timers[id]);
+ if (cb) {
+ (*cb)(data);
+ }
+ }
+ }
+ return 0;
+
+ default:
+ break;
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+
+void Fl_WinAPI_Screen_Driver::add_timeout(double time, Fl_Timeout_Handler cb, void* data)
+{
+ repeat_timeout(time, cb, data);
+}
+
+
+void Fl_WinAPI_Screen_Driver::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data)
+{
+ int timer_id = -1;
+ for (int i = 0; i < win32_timer_used; ++i) {
+ if ( !win32_timers[i].handle ) {
+ timer_id = i;
+ break;
+ }
+ }
+ if (timer_id == -1) {
+ if (win32_timer_used == win32_timer_alloc) {
+ realloc_timers();
+ }
+ timer_id = win32_timer_used++;
+ }
+ unsigned int elapsed = (unsigned int)(time * 1000);
+
+ if ( !s_TimerWnd ) {
+ const char* timer_class = "FLTimer";
+ WNDCLASSEX wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.cbSize = sizeof (wc);
+ wc.style = CS_CLASSDC;
+ wc.lpfnWndProc = (WNDPROC)s_TimerProc;
+ wc.hInstance = fl_display;
+ wc.lpszClassName = timer_class;
+ /*ATOM atom =*/ RegisterClassEx(&wc);
+ // create a zero size window to handle timer events
+ s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW,
+ timer_class, "",
+ WS_POPUP,
+ 0, 0, 0, 0,
+ NULL, NULL, fl_display, NULL);
+ // just in case this OS won't let us create a 0x0 size window:
+ if (!s_TimerWnd)
+ s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW,
+ timer_class, "",
+ WS_POPUP,
+ 0, 0, 1, 1,
+ NULL, NULL, fl_display, NULL);
+ ShowWindow(s_TimerWnd, SW_SHOWNOACTIVATE);
+ }
+
+ win32_timers[timer_id].callback = cb;
+ win32_timers[timer_id].data = data;
+
+ win32_timers[timer_id].handle =
+ SetTimer(s_TimerWnd, timer_id + 1, elapsed, NULL);
+}
+
+
+int Fl_WinAPI_Screen_Driver::has_timeout(Fl_Timeout_Handler cb, void* data)
+{
+ for (int i = 0; i < win32_timer_used; ++i) {
+ Win32Timer& t = win32_timers[i];
+ if (t.handle && t.callback == cb && t.data == data) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+void Fl_WinAPI_Screen_Driver::remove_timeout(Fl_Timeout_Handler cb, void* data)
+{
+ int i;
+ for (i = 0; i < win32_timer_used; ++i) {
+ Win32Timer& t = win32_timers[i];
+ if (t.handle && t.callback == cb &&
+ (t.data == data || data == NULL)) {
+ delete_timer(t);
+ }
+ }
+}
+
+int Fl_WinAPI_Screen_Driver::compose(int &del) {
+ unsigned char ascii = (unsigned char)Fl::e_text[0];
+ int condition = (Fl::e_state & (FL_ALT | FL_META)) && !(ascii & 128) ;
+ if (condition) { // this stuff is to be treated as a function key
+ del = 0;
+ return 0;
+ }
+ del = Fl::compose_state;
+ Fl::compose_state = 0;
+ // Only insert non-control characters:
+ if ( (!Fl::compose_state) && ! (ascii & ~31 && ascii!=127)) {
+ return 0;
+ }
+ return 1;
+}
+
+
+Fl_RGB_Image * // O - image or NULL if failed
+Fl_WinAPI_Screen_Driver::read_win_rectangle(
+ int X, // I - Left position
+ int Y, // I - Top position
+ int w, // I - Width of area to read
+ int h) // I - Height of area to read
+{
+ float s = Fl_Surface_Device::surface()->driver()->scale();
+ return read_win_rectangle_unscaled(X*s, Y*s, w*s, h*s);
+}
+
+Fl_RGB_Image *Fl_WinAPI_Screen_Driver::read_win_rectangle_unscaled(int X, int Y, int w, int h)
+{
+ int d = 3; // Depth of image
+ int alpha = 0; uchar *p = NULL;
+ // Allocate the image data array as needed...
+ const uchar *oldp = p;
+ if (!p) p = new uchar[w * h * d];
+
+ // Initialize the default colors/alpha in the whole image...
+ memset(p, alpha, w * h * d);
+
+ // Grab all of the pixels in the image...
+
+ // Assure that we are not trying to read non-existing data. If it is so, the
+ // function should still work, but the out-of-bounds part of the image is
+ // untouched (initialized with the alpha value or 0 (black), resp.).
+
+ int ww = w; // We need the original width for output data line size
+
+ int shift_x = 0; // X target shift if X modified
+ int shift_y = 0; // Y target shift if X modified
+
+ if (X < 0) {
+ shift_x = -X;
+ w += X;
+ X = 0;
+ }
+ if (Y < 0) {
+ shift_y = -Y;
+ h += Y;
+ Y = 0;
+ }
+
+ if (h < 1 || w < 1) return 0/*p*/; // nothing to copy
+
+ int line_size = ((3*w+3)/4) * 4; // each line is aligned on a DWORD (4 bytes)
+ uchar *dib = new uchar[line_size*h]; // create temporary buffer to read DIB
+
+ // fill in bitmap info for GetDIBits
+
+ BITMAPINFO bi;
+ bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bi.bmiHeader.biWidth = w;
+ bi.bmiHeader.biHeight = -h; // negative => top-down DIB
+ bi.bmiHeader.biPlanes = 1;
+ bi.bmiHeader.biBitCount = 24; // 24 bits RGB
+ bi.bmiHeader.biCompression = BI_RGB;
+ bi.bmiHeader.biSizeImage = 0;
+ bi.bmiHeader.biXPelsPerMeter = 0;
+ bi.bmiHeader.biYPelsPerMeter = 0;
+ bi.bmiHeader.biClrUsed = 0;
+ bi.bmiHeader.biClrImportant = 0;
+
+ // copy bitmap from original DC (Window, Fl_Offscreen, ...)
+ HDC gc = (HDC)fl_graphics_driver->gc();
+ HDC hdc = CreateCompatibleDC(gc);
+ HBITMAP hbm = CreateCompatibleBitmap(gc,w,h);
+
+ int save_dc = SaveDC(hdc); // save context for cleanup
+ SelectObject(hdc,hbm); // select bitmap
+ BitBlt(hdc,0,0,w,h,gc,X,Y,SRCCOPY); // copy image section to DDB
+
+ // copy RGB image data to the allocated DIB
+
+ GetDIBits(hdc, hbm, 0, h, dib, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
+
+ // finally copy the image data to the user buffer
+
+ for (int j = 0; j<h; j++) {
+ const uchar *src = dib + j * line_size; // source line
+ uchar *tg = p + (j + shift_y) * d * ww + shift_x * d; // target line
+ for (int i = 0; i<w; i++) {
+ uchar b = *src++;
+ uchar g = *src++;
+ *tg++ = *src++; // R
+ *tg++ = g; // G
+ *tg++ = b; // B
+ if (alpha)
+ *tg++ = alpha; // alpha
+ }
+ }
+
+ // free used GDI and other structures
+
+ RestoreDC(hdc,save_dc); // reset DC
+ DeleteDC(hdc);
+ DeleteObject(hbm);
+ delete[] dib; // delete DIB temporary buffer
+
+ Fl_RGB_Image *rgb = new Fl_RGB_Image(p, w, h, d);
+ if (!oldp) rgb->alloc_array = 1;
+ return rgb;
+}
+
+#ifndef FLTK_HIDPI_SUPPORT
+/* Returns the current desktop scaling factor for screen_num (1.75 for example)
+ */
+float Fl_WinAPI_Screen_Driver::DWM_scaling_factor() {
+ // Compute the global desktop scaling factor: 1, 1.25, 1.5, 1.75, etc...
+ // This factor can be set in Windows 10 by
+ // "Change the size of text, apps and other items" in display settings.
+ // We don't cache this value because it can change while the app is running.
+ HDC hdc = GetDC(NULL);
+ int hr = GetDeviceCaps(hdc, HORZRES); // pixels visible to the app
+#ifndef DESKTOPHORZRES
+#define DESKTOPHORZRES 118
+ /* As of 27 august 2016, the DESKTOPHORZRES flag for GetDeviceCaps()
+ has disappeared from Microsoft online doc, but is quoted in numerous coding examples
+ e.g., https://social.msdn.microsoft.com/Forums/en-US/6acc3b21-23a4-4a00-90b4-968a43e1ccc8/capture-screen-with-high-dpi?forum=vbgeneral
+ It is necessary for the computation of the scaling factor at runtime as done here.
+ */
+#endif
+ int dhr = GetDeviceCaps(hdc, DESKTOPHORZRES); // true number of pixels on display
+ ReleaseDC(NULL, hdc);
+ float scaling = dhr/float(hr);
+ scaling = int(scaling * 100 + 0.5)/100.; // round to 2 digits after decimal point
+ return scaling;
+}
+
+#endif // ! FLTK_HIDPI_SUPPORT
+
+void Fl_WinAPI_Screen_Driver::offscreen_size(Fl_Offscreen off, int &width, int &height)
+{
+ BITMAP bitmap;
+ if ( GetObject(off, sizeof(BITMAP), &bitmap) ) {
+ width = bitmap.bmWidth;
+ height = bitmap.bmHeight;
+ }
+}
+
+//NOTICE: returns -1 if x,y is not in any screen
+int Fl_WinAPI_Screen_Driver::screen_num_unscaled(int x, int y)
+{
+ int screen = -1;
+ if (num_screens < 0) init();
+ for (int i = 0; i < num_screens; i ++) {
+ if (x >= screens[i].left && x < screens[i].right &&
+ y >= screens[i].top && y < screens[i].bottom) {
+ screen = i;
+ break;
+ }
+ }
+ return screen;
+}
+
+#endif
+
+
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <jni.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+//#include "android_native_app_glue.h"
+#include <android/log.h>
+
+#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
+#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
+
+/* For debug builds, always enable the debug traces in this library */
+#ifndef NDEBUG
+# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__))
+#else
+# define LOGV(...) ((void)0)
+#endif
+
+static void free_saved_state(struct android_app* android_app) {
+ pthread_mutex_lock(&android_app->mutex);
+ if (android_app->savedState != NULL) {
+ free(android_app->savedState);
+ android_app->savedState = NULL;
+ android_app->savedStateSize = 0;
+ }
+ pthread_mutex_unlock(&android_app->mutex);
+}
+
+int8_t android_app_read_cmd(struct android_app* android_app) {
+ int8_t cmd;
+ if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
+ switch (cmd) {
+ case APP_CMD_SAVE_STATE:
+ free_saved_state(android_app);
+ break;
+ }
+ return cmd;
+ } else {
+ LOGE("No data on command pipe!");
+ }
+ return -1;
+}
+
+static void print_cur_config(struct android_app* android_app) {
+ char lang[2], country[2];
+ AConfiguration_getLanguage(android_app->config, lang);
+ AConfiguration_getCountry(android_app->config, country);
+
+ LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
+ "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
+ "modetype=%d modenight=%d",
+ AConfiguration_getMcc(android_app->config),
+ AConfiguration_getMnc(android_app->config),
+ lang[0], lang[1], country[0], country[1],
+ AConfiguration_getOrientation(android_app->config),
+ AConfiguration_getTouchscreen(android_app->config),
+ AConfiguration_getDensity(android_app->config),
+ AConfiguration_getKeyboard(android_app->config),
+ AConfiguration_getNavigation(android_app->config),
+ AConfiguration_getKeysHidden(android_app->config),
+ AConfiguration_getNavHidden(android_app->config),
+ AConfiguration_getSdkVersion(android_app->config),
+ AConfiguration_getScreenSize(android_app->config),
+ AConfiguration_getScreenLong(android_app->config),
+ AConfiguration_getUiModeType(android_app->config),
+ AConfiguration_getUiModeNight(android_app->config));
+}
+
+void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
+ switch (cmd) {
+ case APP_CMD_INPUT_CHANGED:
+ LOGV("APP_CMD_INPUT_CHANGED\n");
+ pthread_mutex_lock(&android_app->mutex);
+ if (android_app->inputQueue != NULL) {
+ AInputQueue_detachLooper(android_app->inputQueue);
+ }
+ android_app->inputQueue = android_app->pendingInputQueue;
+ if (android_app->inputQueue != NULL) {
+ LOGV("Attaching input queue to looper");
+ AInputQueue_attachLooper(android_app->inputQueue,
+ android_app->looper, LOOPER_ID_INPUT, NULL,
+ &android_app->inputPollSource);
+ }
+ pthread_cond_broadcast(&android_app->cond);
+ pthread_mutex_unlock(&android_app->mutex);
+ break;
+
+ case APP_CMD_INIT_WINDOW:
+ LOGV("APP_CMD_INIT_WINDOW\n");
+ pthread_mutex_lock(&android_app->mutex);
+ android_app->window = android_app->pendingWindow;
+ pthread_cond_broadcast(&android_app->cond);
+ pthread_mutex_unlock(&android_app->mutex);
+ break;
+
+ case APP_CMD_TERM_WINDOW:
+ LOGV("APP_CMD_TERM_WINDOW\n");
+ pthread_cond_broadcast(&android_app->cond);
+ break;
+
+ case APP_CMD_RESUME:
+ case APP_CMD_START:
+ case APP_CMD_PAUSE:
+ case APP_CMD_STOP:
+ LOGV("activityState=%d\n", cmd);
+ pthread_mutex_lock(&android_app->mutex);
+ android_app->activityState = cmd;
+ pthread_cond_broadcast(&android_app->cond);
+ pthread_mutex_unlock(&android_app->mutex);
+ break;
+
+ case APP_CMD_CONFIG_CHANGED:
+ LOGV("APP_CMD_CONFIG_CHANGED\n");
+ AConfiguration_fromAssetManager(android_app->config,
+ android_app->activity->assetManager);
+ print_cur_config(android_app);
+ break;
+
+ case APP_CMD_DESTROY:
+ LOGV("APP_CMD_DESTROY\n");
+ android_app->destroyRequested = 1;
+ break;
+ }
+}
+
+void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
+ switch (cmd) {
+ case APP_CMD_TERM_WINDOW:
+ LOGV("APP_CMD_TERM_WINDOW\n");
+ pthread_mutex_lock(&android_app->mutex);
+ android_app->window = NULL;
+ pthread_cond_broadcast(&android_app->cond);
+ pthread_mutex_unlock(&android_app->mutex);
+ break;
+
+ case APP_CMD_SAVE_STATE:
+ LOGV("APP_CMD_SAVE_STATE\n");
+ pthread_mutex_lock(&android_app->mutex);
+ android_app->stateSaved = 1;
+ pthread_cond_broadcast(&android_app->cond);
+ pthread_mutex_unlock(&android_app->mutex);
+ break;
+
+ case APP_CMD_RESUME:
+ free_saved_state(android_app);
+ break;
+ }
+}
+
+void app_dummy() {
+
+}
+
+static void android_app_destroy(struct android_app* android_app) {
+ LOGV("android_app_destroy!");
+ free_saved_state(android_app);
+ pthread_mutex_lock(&android_app->mutex);
+ if (android_app->inputQueue != NULL) {
+ AInputQueue_detachLooper(android_app->inputQueue);
+ }
+ AConfiguration_delete(android_app->config);
+ android_app->destroyed = 1;
+ pthread_cond_broadcast(&android_app->cond);
+ pthread_mutex_unlock(&android_app->mutex);
+ // Can't touch android_app object after this.
+}
+
+static void process_input(struct android_app* app, struct android_poll_source* source) {
+ AInputEvent* event = NULL;
+ while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
+ LOGV("New input event: type=%d\n", AInputEvent_getType(event));
+ if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
+ continue;
+ }
+ int32_t handled = 0;
+ if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
+ AInputQueue_finishEvent(app->inputQueue, event, handled);
+ }
+}
+
+static void process_cmd(struct android_app* app, struct android_poll_source* source) {
+ int8_t cmd = android_app_read_cmd(app);
+ android_app_pre_exec_cmd(app, cmd);
+ if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
+ android_app_post_exec_cmd(app, cmd);
+}
+
+static void* android_app_entry(void* param) {
+ struct android_app* android_app = (struct android_app*)param;
+
+ android_app->config = AConfiguration_new();
+ AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
+
+ print_cur_config(android_app);
+
+ android_app->cmdPollSource.id = LOOPER_ID_MAIN;
+ android_app->cmdPollSource.app = android_app;
+ android_app->cmdPollSource.process = process_cmd;
+ android_app->inputPollSource.id = LOOPER_ID_INPUT;
+ android_app->inputPollSource.app = android_app;
+ android_app->inputPollSource.process = process_input;
+
+ ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+ ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
+ &android_app->cmdPollSource);
+ android_app->looper = looper;
+
+ pthread_mutex_lock(&android_app->mutex);
+ android_app->running = 1;
+ pthread_cond_broadcast(&android_app->cond);
+ pthread_mutex_unlock(&android_app->mutex);
+
+ android_main(android_app);
+
+ android_app_destroy(android_app);
+ return NULL;
+}
+
+// --------------------------------------------------------------------
+// Native activity interaction (called from main thread)
+// --------------------------------------------------------------------
+
+static struct android_app* android_app_create(ANativeActivity* activity,
+ void* savedState, size_t savedStateSize) {
+ struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
+ memset(android_app, 0, sizeof(struct android_app));
+ android_app->activity = activity;
+
+ pthread_mutex_init(&android_app->mutex, NULL);
+ pthread_cond_init(&android_app->cond, NULL);
+
+ if (savedState != NULL) {
+ android_app->savedState = malloc(savedStateSize);
+ android_app->savedStateSize = savedStateSize;
+ memcpy(android_app->savedState, savedState, savedStateSize);
+ }
+
+ int msgpipe[2];
+ if (pipe(msgpipe)) {
+ LOGE("could not create pipe: %s", strerror(errno));
+ return NULL;
+ }
+ android_app->msgread = msgpipe[0];
+ android_app->msgwrite = msgpipe[1];
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
+
+ // Wait for thread to start.
+ pthread_mutex_lock(&android_app->mutex);
+ while (!android_app->running) {
+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
+ }
+ pthread_mutex_unlock(&android_app->mutex);
+
+ return android_app;
+}
+
+static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
+ if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
+ LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
+ }
+}
+
+static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
+ pthread_mutex_lock(&android_app->mutex);
+ android_app->pendingInputQueue = inputQueue;
+ android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
+ while (android_app->inputQueue != android_app->pendingInputQueue) {
+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
+ }
+ pthread_mutex_unlock(&android_app->mutex);
+}
+
+static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
+ pthread_mutex_lock(&android_app->mutex);
+ if (android_app->pendingWindow != NULL) {
+ android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
+ }
+ android_app->pendingWindow = window;
+ if (window != NULL) {
+ android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
+ }
+ while (android_app->window != android_app->pendingWindow) {
+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
+ }
+ pthread_mutex_unlock(&android_app->mutex);
+}
+
+static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
+ pthread_mutex_lock(&android_app->mutex);
+ android_app_write_cmd(android_app, cmd);
+ while (android_app->activityState != cmd) {
+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
+ }
+ pthread_mutex_unlock(&android_app->mutex);
+}
+
+static void android_app_free(struct android_app* android_app) {
+ pthread_mutex_lock(&android_app->mutex);
+ android_app_write_cmd(android_app, APP_CMD_DESTROY);
+ while (!android_app->destroyed) {
+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
+ }
+ pthread_mutex_unlock(&android_app->mutex);
+
+ close(android_app->msgread);
+ close(android_app->msgwrite);
+ pthread_cond_destroy(&android_app->cond);
+ pthread_mutex_destroy(&android_app->mutex);
+ free(android_app);
+}
+
+static void onDestroy(ANativeActivity* activity) {
+ LOGV("Destroy: %p\n", activity);
+ android_app_free((struct android_app*)activity->instance);
+}
+
+static void onStart(ANativeActivity* activity) {
+ LOGV("Start: %p\n", activity);
+ android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
+}
+
+static void onResume(ANativeActivity* activity) {
+ LOGV("Resume: %p\n", activity);
+ android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
+}
+
+static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
+ struct android_app* android_app = (struct android_app*)activity->instance;
+ void* savedState = NULL;
+
+ LOGV("SaveInstanceState: %p\n", activity);
+ pthread_mutex_lock(&android_app->mutex);
+ android_app->stateSaved = 0;
+ android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
+ while (!android_app->stateSaved) {
+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
+ }
+
+ if (android_app->savedState != NULL) {
+ savedState = android_app->savedState;
+ *outLen = android_app->savedStateSize;
+ android_app->savedState = NULL;
+ android_app->savedStateSize = 0;
+ }
+
+ pthread_mutex_unlock(&android_app->mutex);
+
+ return savedState;
+}
+
+static void onPause(ANativeActivity* activity) {
+ LOGV("Pause: %p\n", activity);
+ android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
+}
+
+static void onStop(ANativeActivity* activity) {
+ LOGV("Stop: %p\n", activity);
+ android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
+}
+
+static void onConfigurationChanged(ANativeActivity* activity) {
+ struct android_app* android_app = (struct android_app*)activity->instance;
+ LOGV("ConfigurationChanged: %p\n", activity);
+ android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
+}
+
+static void onLowMemory(ANativeActivity* activity) {
+ struct android_app* android_app = (struct android_app*)activity->instance;
+ LOGV("LowMemory: %p\n", activity);
+ android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
+}
+
+static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
+ LOGV("WindowFocusChanged: %p -- %d\n", activity, focused);
+ android_app_write_cmd((struct android_app*)activity->instance,
+ focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
+}
+
+static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
+ LOGV("NativeWindowCreated: %p -- %p\n", activity, window);
+ android_app_set_window((struct android_app*)activity->instance, window);
+}
+
+static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
+ LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window);
+ android_app_set_window((struct android_app*)activity->instance, NULL);
+}
+
+static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
+ LOGV("InputQueueCreated: %p -- %p\n", activity, queue);
+ android_app_set_input((struct android_app*)activity->instance, queue);
+}
+
+static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
+ LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue);
+ android_app_set_input((struct android_app*)activity->instance, NULL);
+}
+
+JNIEXPORT
+void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState,
+ size_t savedStateSize) {
+ LOGV("Creating: %p\n", activity);
+ activity->callbacks->onDestroy = onDestroy;
+ activity->callbacks->onStart = onStart;
+ activity->callbacks->onResume = onResume;
+ activity->callbacks->onSaveInstanceState = onSaveInstanceState;
+ activity->callbacks->onPause = onPause;
+ activity->callbacks->onStop = onStop;
+ activity->callbacks->onConfigurationChanged = onConfigurationChanged;
+ activity->callbacks->onLowMemory = onLowMemory;
+ activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
+ activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
+ activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
+ activity->callbacks->onInputQueueCreated = onInputQueueCreated;
+ activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
+
+ activity->instance = android_app_create(activity, savedState, savedStateSize);
+}
+
+
+//
+// End of "$Id: Fl_Android_Screen_Driver.cxx 12655 2018-02-09 14:39:42Z AlbrechtS $".
+//