summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-12-13 10:58:59 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-12-23 13:34:57 +0200
commite39721581f082a20f1ef3a6f81b83c5e489cf7c7 (patch)
treece16e9a85d344d21eaa146f49e237eeaa3d1a1b9
parent74de1ab589b80b7719e97341ec98869ecf3da8aa (diff)
Android: Various fixes to get things up and running
Resource paths, runtime data, ignore mouse events. Assume that the Java side tells us the display pixel density via a command line argument.
-rw-r--r--src/app.c39
-rw-r--r--src/resources.c20
-rw-r--r--src/ui/documentwidget.c68
-rw-r--r--src/ui/touch.c4
-rw-r--r--src/ui/window.c10
5 files changed, 99 insertions, 42 deletions
diff --git a/src/app.c b/src/app.c
index eea3689f..3ee2345c 100644
--- a/src/app.c
+++ b/src/app.c
@@ -92,7 +92,10 @@ static const char *defaultDataDir_App_ = "~/Library/Application Support";
92#define EMB_BIN "../resources.lgr" 92#define EMB_BIN "../resources.lgr"
93static const char *defaultDataDir_App_ = "~/AppData/Roaming/fi.skyjake.Lagrange"; 93static const char *defaultDataDir_App_ = "~/AppData/Roaming/fi.skyjake.Lagrange";
94#endif 94#endif
95#if defined (iPlatformLinux) || defined (iPlatformOther) 95#if defined (iPlatformAndroidMobile)
96#define EMB_BIN "resources.lgr" /* loaded from assets with SDL_rwops */
97static const char *defaultDataDir_App_ = NULL; /* will ask SDL */
98#elif defined (iPlatformLinux) || defined (iPlatformOther)
96#define EMB_BIN "../../share/lagrange/resources.lgr" 99#define EMB_BIN "../../share/lagrange/resources.lgr"
97#define EMB_BIN2 "../../../share/lagrange/resources.lgr" 100#define EMB_BIN2 "../../../share/lagrange/resources.lgr"
98static const char *defaultDataDir_App_ = "~/.config/lagrange"; 101static const char *defaultDataDir_App_ = "~/.config/lagrange";
@@ -139,6 +142,9 @@ struct Impl_App {
139 int autoReloadTimer; 142 int autoReloadTimer;
140 iPeriodic periodic; 143 iPeriodic periodic;
141 int warmupFrames; /* forced refresh just after resuming from background; FIXME: shouldn't be needed */ 144 int warmupFrames; /* forced refresh just after resuming from background; FIXME: shouldn't be needed */
145#if defined (iPlatformAndroidMobile)
146 float displayDensity;
147#endif
142 /* Preferences: */ 148 /* Preferences: */
143 iBool commandEcho; /* --echo */ 149 iBool commandEcho; /* --echo */
144 iBool forceSoftwareRender; /* --sw */ 150 iBool forceSoftwareRender; /* --sw */
@@ -307,7 +313,10 @@ static const char *dataDir_App_(void) {
307 return userDir; 313 return userDir;
308 } 314 }
309#endif 315#endif
310 return defaultDataDir_App_; 316 if (defaultDataDir_App_) {
317 return defaultDataDir_App_;
318 }
319 return SDL_GetPrefPath("Jaakko Keränen", "fi.skyjake.lagrange");
311} 320}
312 321
313static const char *downloadDir_App_(void) { 322static const char *downloadDir_App_(void) {
@@ -715,7 +724,7 @@ static iBool hasCommandLineOpenableScheme_(const iRangecc uri) {
715} 724}
716 725
717static void init_App_(iApp *d, int argc, char **argv) { 726static void init_App_(iApp *d, int argc, char **argv) {
718#if defined (iPlatformLinux) 727#if defined (iPlatformLinux) && !defined (iPlatformAndroid)
719 d->isRunningUnderWindowSystem = !iCmpStr(SDL_GetCurrentVideoDriver(), "x11") || 728 d->isRunningUnderWindowSystem = !iCmpStr(SDL_GetCurrentVideoDriver(), "x11") ||
720 !iCmpStr(SDL_GetCurrentVideoDriver(), "wayland"); 729 !iCmpStr(SDL_GetCurrentVideoDriver(), "wayland");
721#else 730#else
@@ -763,6 +772,8 @@ static void init_App_(iApp *d, int argc, char **argv) {
763 } 772 }
764 } 773 }
765 init_Lang(); 774 init_Lang();
775 iStringList *openCmds = new_StringList();
776#if !defined (iPlatformAndroidMobile)
766 /* Configure the valid command line options. */ { 777 /* Configure the valid command line options. */ {
767 defineValues_CommandLine(&d->args, "close-tab", 0); 778 defineValues_CommandLine(&d->args, "close-tab", 0);
768 defineValues_CommandLine(&d->args, "echo;E", 0); 779 defineValues_CommandLine(&d->args, "echo;E", 0);
@@ -777,7 +788,6 @@ static void init_App_(iApp *d, int argc, char **argv) {
777 defineValues_CommandLine(&d->args, "sw", 0); 788 defineValues_CommandLine(&d->args, "sw", 0);
778 defineValues_CommandLine(&d->args, "version;V", 0); 789 defineValues_CommandLine(&d->args, "version;V", 0);
779 } 790 }
780 iStringList *openCmds = new_StringList();
781 /* Handle command line options. */ { 791 /* Handle command line options. */ {
782 if (contains_CommandLine(&d->args, "help")) { 792 if (contains_CommandLine(&d->args, "help")) {
783 puts(cstr_Block(&blobArghelp_Resources)); 793 puts(cstr_Block(&blobArghelp_Resources));
@@ -826,6 +836,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
826 } 836 }
827 } 837 }
828 } 838 }
839#endif
829#if defined (LAGRANGE_ENABLE_IPC) 840#if defined (LAGRANGE_ENABLE_IPC)
830 /* Only one instance is allowed to run at a time; the runtime files (bookmarks, etc.) 841 /* Only one instance is allowed to run at a time; the runtime files (bookmarks, etc.)
831 are not shareable. */ { 842 are not shareable. */ {
@@ -860,7 +871,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
860 /* Must scale by UI scaling factor. */ 871 /* Must scale by UI scaling factor. */
861 mulfv_I2(&d->initialWindowRect.size, desktopDPI_Win32()); 872 mulfv_I2(&d->initialWindowRect.size, desktopDPI_Win32());
862#endif 873#endif
863#if defined (iPlatformLinux) 874#if defined (iPlatformLinux) && !defined (iPlatformAndroid)
864 /* Scale by the primary (?) monitor DPI. */ 875 /* Scale by the primary (?) monitor DPI. */
865 if (isRunningUnderWindowSystem_App()) { 876 if (isRunningUnderWindowSystem_App()) {
866 float vdpi; 877 float vdpi;
@@ -1325,6 +1336,15 @@ void processEvents_App(enum iAppEventMode eventMode) {
1325 } 1336 }
1326 ev.key.keysym.mod = mapMods_Keys(ev.key.keysym.mod & ~KMOD_CAPS); 1337 ev.key.keysym.mod = mapMods_Keys(ev.key.keysym.mod & ~KMOD_CAPS);
1327 } 1338 }
1339#if defined (iPlatformAndroidMobile)
1340 /* Ignore all mouse events; just use touch. */
1341 if (ev.type == SDL_MOUSEBUTTONDOWN ||
1342 ev.type == SDL_MOUSEBUTTONUP ||
1343 ev.type == SDL_MOUSEMOTION ||
1344 ev.type == SDL_MOUSEWHEEL) {
1345 continue;
1346 }
1347#endif
1328 /* Scroll events may be per-pixel or mouse wheel steps. */ 1348 /* Scroll events may be per-pixel or mouse wheel steps. */
1329 if (ev.type == SDL_MOUSEWHEEL) { 1349 if (ev.type == SDL_MOUSEWHEEL) {
1330#if defined (iPlatformMsys) 1350#if defined (iPlatformMsys)
@@ -1773,6 +1793,8 @@ enum iAppDeviceType deviceType_App(void) {
1773 return tablet_AppDeviceType; 1793 return tablet_AppDeviceType;
1774#elif defined (iPlatformAppleMobile) 1794#elif defined (iPlatformAppleMobile)
1775 return isPhone_iOS() ? phone_AppDeviceType : tablet_AppDeviceType; 1795 return isPhone_iOS() ? phone_AppDeviceType : tablet_AppDeviceType;
1796#elif defined (iPlatformAndroidMobile)
1797 return phone_AppDeviceType; /* TODO: Java side could tell us via cmdline if this is a tablet. */
1776#else 1798#else
1777 return desktop_AppDeviceType; 1799 return desktop_AppDeviceType;
1778#endif 1800#endif
@@ -3408,3 +3430,10 @@ void closePopups_App(void) {
3408 } 3430 }
3409 } 3431 }
3410} 3432}
3433
3434#if defined (iPlatformAndroidMobile)
3435float displayDensity_Android(void) {
3436 iApp *d = &app_;
3437 return toFloat_String(at_CommandLine(&d->args, 1));
3438}
3439#endif
diff --git a/src/resources.c b/src/resources.c
index bb601cca..e3d92946 100644
--- a/src/resources.c
+++ b/src/resources.c
@@ -26,7 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
26#include <the_Foundation/version.h> 26#include <the_Foundation/version.h>
27 27
28static iArchive *archive_; 28static iArchive *archive_;
29 29
30iBlock blobAbout_Resources; 30iBlock blobAbout_Resources;
31iBlock blobHelp_Resources; 31iBlock blobHelp_Resources;
32iBlock blobLagrange_Resources; 32iBlock blobLagrange_Resources;
@@ -101,7 +101,23 @@ static struct {
101 101
102iBool init_Resources(const char *path) { 102iBool init_Resources(const char *path) {
103 archive_ = new_Archive(); 103 archive_ = new_Archive();
104 if (openFile_Archive(archive_, collectNewCStr_String(path))) { 104 iBool ok = iFalse;
105#if defined (iPlatformAndroidMobile)
106 /* Resources are bundled as assets so they cannot be loaded as a regular file.
107 Fortunately, SDL implements a file wrapper. */
108 SDL_RWops *io = SDL_RWFromFile(path, "rb");
109 if (io) {
110 iBlock buf;
111 init_Block(&buf, (size_t) SDL_RWsize(io));
112 SDL_RWread(io, data_Block(&buf), size_Block(&buf), 1);
113 SDL_RWclose(io);
114 ok = openData_Archive(archive_, &buf);
115 deinit_Block(&buf);
116 }
117#else
118 ok = openFile_Archive(archive_, collectNewCStr_String(path));
119#endif
120 if (ok) {
105 iVersion appVer; 121 iVersion appVer;
106 init_Version(&appVer, range_CStr(LAGRANGE_APP_VERSION)); 122 init_Version(&appVer, range_CStr(LAGRANGE_APP_VERSION));
107 iVersion resVer; 123 iVersion resVer;
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index e93fb586..b20ae672 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -225,7 +225,7 @@ enum iDocumentWidgetFlag {
225 animationPlaceholder_DocumentWidgetFlag = iBit(16), /* avoid slow operations */ 225 animationPlaceholder_DocumentWidgetFlag = iBit(16), /* avoid slow operations */
226 invalidationPending_DocumentWidgetFlag = iBit(17), /* invalidate as soon as convenient */ 226 invalidationPending_DocumentWidgetFlag = iBit(17), /* invalidate as soon as convenient */
227 leftWheelSwipe_DocumentWidgetFlag = iBit(18), /* swipe state flags are used on desktop */ 227 leftWheelSwipe_DocumentWidgetFlag = iBit(18), /* swipe state flags are used on desktop */
228 rightWheelSwipe_DocumentWidgetFlag = iBit(19), 228 rightWheelSwipe_DocumentWidgetFlag = iBit(19),
229 eitherWheelSwipe_DocumentWidgetFlag = leftWheelSwipe_DocumentWidgetFlag | 229 eitherWheelSwipe_DocumentWidgetFlag = leftWheelSwipe_DocumentWidgetFlag |
230 rightWheelSwipe_DocumentWidgetFlag, 230 rightWheelSwipe_DocumentWidgetFlag,
231}; 231};
@@ -242,8 +242,8 @@ enum iWheelSwipeState {
242 242
243/* TODO: DocumentView is supposed to be useful on its own; move to a separate source file. */ 243/* TODO: DocumentView is supposed to be useful on its own; move to a separate source file. */
244iDeclareType(DocumentView) 244iDeclareType(DocumentView)
245 245
246struct Impl_DocumentView { 246struct Impl_DocumentView {
247 iDocumentWidget *owner; /* TODO: Convert to an abstract provider of metrics? */ 247 iDocumentWidget *owner; /* TODO: Convert to an abstract provider of metrics? */
248 iGmDocument * doc; 248 iGmDocument * doc;
249 int pageMargin; 249 int pageMargin;
@@ -253,7 +253,7 @@ struct Impl_DocumentView {
253 iGmRunRange visibleRuns; 253 iGmRunRange visibleRuns;
254 iPtrArray visibleLinks; 254 iPtrArray visibleLinks;
255 iPtrArray visiblePre; 255 iPtrArray visiblePre;
256 iPtrArray visibleMedia; /* currently playing audio / ongoing downloads */ 256 iPtrArray visibleMedia; /* currently playing audio / ongoing downloads */
257 iPtrArray visibleWideRuns; /* scrollable blocks; TODO: merge into `visiblePre` */ 257 iPtrArray visibleWideRuns; /* scrollable blocks; TODO: merge into `visiblePre` */
258 const iGmRun * hoverPre; /* for clicking */ 258 const iGmRun * hoverPre; /* for clicking */
259 const iGmRun * hoverAltPre; /* for drawing alt text */ 259 const iGmRun * hoverAltPre; /* for drawing alt text */
@@ -263,7 +263,7 @@ struct Impl_DocumentView {
263 uint16_t animWideRunId; 263 uint16_t animWideRunId;
264 iGmRunRange animWideRunRange; 264 iGmRunRange animWideRunRange;
265 iDrawBufs * drawBufs; /* dynamic state for drawing */ 265 iDrawBufs * drawBufs; /* dynamic state for drawing */
266 iVisBuf * visBuf; 266 iVisBuf * visBuf;
267 iVisBufMeta * visBufMeta; 267 iVisBufMeta * visBufMeta;
268 iGmRunRange renderRuns; 268 iGmRunRange renderRuns;
269 iPtrSet * invalidRuns; 269 iPtrSet * invalidRuns;
@@ -272,7 +272,7 @@ struct Impl_DocumentView {
272struct Impl_DocumentWidget { 272struct Impl_DocumentWidget {
273 iWidget widget; 273 iWidget widget;
274 int flags; /* internal behavior, see enum iDocumentWidgetFlag */ 274 int flags; /* internal behavior, see enum iDocumentWidgetFlag */
275 275
276 /* User interface: */ 276 /* User interface: */
277 enum iDocumentLinkOrdinalMode ordinalMode; 277 enum iDocumentLinkOrdinalMode ordinalMode;
278 size_t ordinalBase; 278 size_t ordinalBase;
@@ -293,7 +293,7 @@ struct Impl_DocumentWidget {
293 enum iWheelSwipeState wheelSwipeState; 293 enum iWheelSwipeState wheelSwipeState;
294 iString pendingGotoHeading; 294 iString pendingGotoHeading;
295 iString linePrecedingLink; 295 iString linePrecedingLink;
296 296
297 /* Network request: */ 297 /* Network request: */
298 enum iRequestState state; 298 enum iRequestState state;
299 iGmRequest * request; 299 iGmRequest * request;
@@ -304,7 +304,7 @@ struct Impl_DocumentWidget {
304 iString * certSubject; 304 iString * certSubject;
305 int redirectCount; 305 int redirectCount;
306 iObjectList * media; /* inline media requests */ 306 iObjectList * media; /* inline media requests */
307 307
308 /* Document: */ 308 /* Document: */
309 iPersistentDocumentState mod; 309 iPersistentDocumentState mod;
310 iString * titleUser; 310 iString * titleUser;
@@ -316,12 +316,12 @@ struct Impl_DocumentWidget {
316 iGempub * sourceGempub; /* NULL unless the page is Gempub content */ 316 iGempub * sourceGempub; /* NULL unless the page is Gempub content */
317 iBanner * banner; 317 iBanner * banner;
318 float initNormScrollY; 318 float initNormScrollY;
319 319
320 /* Rendering: */ 320 /* Rendering: */
321 iDocumentView view; 321 iDocumentView view;
322 iLinkInfo * linkInfo; 322 iLinkInfo * linkInfo;
323 323
324 /* Widget structure: */ 324 /* Widget structure: */
325 iScrollWidget *scroll; 325 iScrollWidget *scroll;
326 iWidget * footerButtons; 326 iWidget * footerButtons;
327 iWidget * menu; 327 iWidget * menu;
@@ -332,7 +332,7 @@ struct Impl_DocumentWidget {
332}; 332};
333 333
334iDefineObjectConstruction(DocumentWidget) 334iDefineObjectConstruction(DocumentWidget)
335 335
336/* Sorted by proximity to F and J. */ 336/* Sorted by proximity to F and J. */
337static const int homeRowKeys_[] = { 337static const int homeRowKeys_[] = {
338 'f', 'd', 's', 'a', 338 'f', 'd', 's', 'a',
@@ -344,7 +344,7 @@ static const int homeRowKeys_[] = {
344 'g', 'h', 344 'g', 'h',
345 'b', 345 'b',
346 't', 'y', 346 't', 'y',
347}; 347};
348static int docEnum_ = 0; 348static int docEnum_ = 0;
349 349
350static void animate_DocumentWidget_ (void *ticker); 350static void animate_DocumentWidget_ (void *ticker);
@@ -909,7 +909,7 @@ static void updateTimestampBuf_DocumentView_(const iDocumentView *d) {
909 909
910static void invalidate_DocumentView_(iDocumentView *d) { 910static void invalidate_DocumentView_(iDocumentView *d) {
911 invalidate_VisBuf(d->visBuf); 911 invalidate_VisBuf(d->visBuf);
912 clear_PtrSet(d->invalidRuns); 912 clear_PtrSet(d->invalidRuns);
913} 913}
914 914
915static void documentRunsInvalidated_DocumentView_(iDocumentView *d) { 915static void documentRunsInvalidated_DocumentView_(iDocumentView *d) {
@@ -928,11 +928,11 @@ static void resetScroll_DocumentView_(iDocumentView *d) {
928} 928}
929 929
930static void updateWidth_DocumentView_(iDocumentView *d) { 930static void updateWidth_DocumentView_(iDocumentView *d) {
931 updateWidth_GmDocument(d->doc, documentWidth_DocumentView_(d), width_Widget(d->owner)); 931 updateWidth_GmDocument(d->doc, documentWidth_DocumentView_(d), width_Widget(d->owner));
932} 932}
933 933
934static void updateWidthAndRedoLayout_DocumentView_(iDocumentView *d) { 934static void updateWidthAndRedoLayout_DocumentView_(iDocumentView *d) {
935 setWidth_GmDocument(d->doc, documentWidth_DocumentView_(d), width_Widget(d->owner)); 935 setWidth_GmDocument(d->doc, documentWidth_DocumentView_(d), width_Widget(d->owner));
936} 936}
937 937
938static void clampScroll_DocumentView_(iDocumentView *d) { 938static void clampScroll_DocumentView_(iDocumentView *d) {
@@ -1025,7 +1025,7 @@ static iRangecc sourceLoc_DocumentView_(const iDocumentView *d, iInt2 pos) {
1025} 1025}
1026 1026
1027iDeclareType(MiddleRunParams) 1027iDeclareType(MiddleRunParams)
1028 1028
1029struct Impl_MiddleRunParams { 1029struct Impl_MiddleRunParams {
1030 int midY; 1030 int midY;
1031 const iGmRun *closest; 1031 const iGmRun *closest;
@@ -1126,7 +1126,7 @@ static iRect runRect_DocumentView_(const iDocumentView *d, const iGmRun *run) {
1126} 1126}
1127 1127
1128iDeclareType(DrawContext) 1128iDeclareType(DrawContext)
1129 1129
1130struct Impl_DrawContext { 1130struct Impl_DrawContext {
1131 const iDocumentView *view; 1131 const iDocumentView *view;
1132 iRect widgetBounds; 1132 iRect widgetBounds;
@@ -1260,7 +1260,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
1260 isInlineImageCaption = iFalse; 1260 isInlineImageCaption = iFalse;
1261 } 1261 }
1262#endif 1262#endif
1263 /* While this is consistent, it's a bit excessive to indicate that an inlined image 1263 /* While this is consistent, it's a bit excessive to indicate that an inlined image
1264 is open: the image itself is the indication. */ 1264 is open: the image itself is the indication. */
1265 const iBool isInlineImageCaption = iFalse; 1265 const iBool isInlineImageCaption = iFalse;
1266 if (run->linkId && (linkFlags & isOpen_GmLinkFlag || isInlineImageCaption)) { 1266 if (run->linkId && (linkFlags & isOpen_GmLinkFlag || isInlineImageCaption)) {
@@ -1285,7 +1285,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
1285 } 1285 }
1286 fillRect_Paint(&d->paint, wideRect, bg); 1286 fillRect_Paint(&d->paint, wideRect, bg);
1287 } 1287 }
1288 else { 1288 else {
1289 /* Normal background for other runs. There are cases when runs get drawn multiple times, 1289 /* Normal background for other runs. There are cases when runs get drawn multiple times,
1290 e.g., at the buffer boundary, and there are slightly overlapping characters in 1290 e.g., at the buffer boundary, and there are slightly overlapping characters in
1291 monospace blocks. Clearing the background here ensures a cleaner visual appearance 1291 monospace blocks. Clearing the background here ensures a cleaner visual appearance
@@ -2095,7 +2095,7 @@ static void invalidate_DocumentWidget_(iDocumentWidget *d) {
2095} 2095}
2096 2096
2097static iRangecc siteText_DocumentWidget_(const iDocumentWidget *d) { 2097static iRangecc siteText_DocumentWidget_(const iDocumentWidget *d) {
2098 return isEmpty_String(d->titleUser) ? urlHost_String(d->mod.url) 2098 return isEmpty_String(d->titleUser) ? urlHost_String(d->mod.url)
2099 : range_String(d->titleUser); 2099 : range_String(d->titleUser);
2100} 2100}
2101 2101
@@ -2161,7 +2161,7 @@ static void updateBanner_DocumentWidget_(iDocumentWidget *d) {
2161static void updateTheme_DocumentWidget_(iDocumentWidget *d) { 2161static void updateTheme_DocumentWidget_(iDocumentWidget *d) {
2162 if (document_App() != d || category_GmStatusCode(d->sourceStatus) == categoryInput_GmStatusCode) { 2162 if (document_App() != d || category_GmStatusCode(d->sourceStatus) == categoryInput_GmStatusCode) {
2163 return; 2163 return;
2164 } 2164 }
2165 d->view.drawBufs->flags |= updateTimestampBuf_DrawBufsFlag; 2165 d->view.drawBufs->flags |= updateTimestampBuf_DrawBufsFlag;
2166 updateBanner_DocumentWidget_(d); 2166 updateBanner_DocumentWidget_(d);
2167} 2167}
@@ -2620,7 +2620,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d,
2620 appendFormat_String(&str, 2620 appendFormat_String(&str,
2621 cstr_Lang("doc.archive"), 2621 cstr_Lang("doc.archive"),
2622 cstr_Rangecc(baseName_Path(d->mod.url))); 2622 cstr_Rangecc(baseName_Path(d->mod.url)));
2623 appendCStr_String(&str, "\n"); 2623 appendCStr_String(&str, "\n");
2624 } 2624 }
2625 appendCStr_String(&str, "\n"); 2625 appendCStr_String(&str, "\n");
2626 iString *localPath = localFilePathFromUrl_String(d->mod.url); 2626 iString *localPath = localFilePathFromUrl_String(d->mod.url);
@@ -2768,7 +2768,7 @@ static void updateTrust_DocumentWidget_(iDocumentWidget *d, const iGmResponse *r
2768 } 2768 }
2769 else if (~d->certFlags & timeVerified_GmCertFlag) { 2769 else if (~d->certFlags & timeVerified_GmCertFlag) {
2770 updateTextCStr_LabelWidget(lock, isDarkMode ? orange_ColorEscape warning_Icon 2770 updateTextCStr_LabelWidget(lock, isDarkMode ? orange_ColorEscape warning_Icon
2771 : black_ColorEscape warning_Icon); 2771 : black_ColorEscape warning_Icon);
2772 } 2772 }
2773 else { 2773 else {
2774 updateTextCStr_LabelWidget(lock, green_ColorEscape closedLock_Icon); 2774 updateTextCStr_LabelWidget(lock, green_ColorEscape closedLock_Icon);
@@ -3067,7 +3067,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) {
3067 it is only displayed as an input dialog. */ 3067 it is only displayed as an input dialog. */
3068 visitUrl_Visited(visited_App(), d->mod.url, transient_VisitedUrlFlag); 3068 visitUrl_Visited(visited_App(), d->mod.url, transient_VisitedUrlFlag);
3069 iUrl parts; 3069 iUrl parts;
3070 init_Url(&parts, d->mod.url); 3070 init_Url(&parts, d->mod.url);
3071 iWidget *dlg = makeValueInput_Widget( 3071 iWidget *dlg = makeValueInput_Widget(
3072 as_Widget(d), 3072 as_Widget(d),
3073 NULL, 3073 NULL,
@@ -3132,7 +3132,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) {
3132 setFont_LabelWidget(menu, font_LabelWidget((iLabelWidget *) lastChild_Widget(buttons))); 3132 setFont_LabelWidget(menu, font_LabelWidget((iLabelWidget *) lastChild_Widget(buttons)));
3133 setTextColor_LabelWidget(menu, uiTextAction_ColorId); 3133 setTextColor_LabelWidget(menu, uiTextAction_ColorId);
3134 } 3134 }
3135 } 3135 }
3136 setValidator_InputWidget(findChild_Widget(dlg, "input"), inputQueryValidator_, d); 3136 setValidator_InputWidget(findChild_Widget(dlg, "input"), inputQueryValidator_, d);
3137 setSensitiveContent_InputWidget(findChild_Widget(dlg, "input"), 3137 setSensitiveContent_InputWidget(findChild_Widget(dlg, "input"),
3138 statusCode == sensitiveInput_GmStatusCode); 3138 statusCode == sensitiveInput_GmStatusCode);
@@ -3491,7 +3491,7 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
3491 } 3491 }
3492 /* The temporary "swipein" will display the previous page until the finger is lifted. */ 3492 /* The temporary "swipein" will display the previous page until the finger is lifted. */
3493 iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); 3493 iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein");
3494 if (!swipeIn) { 3494 if (!swipeIn) {
3495 swipeIn = new_DocumentWidget(); 3495 swipeIn = new_DocumentWidget();
3496 swipeIn->flags |= animationPlaceholder_DocumentWidgetFlag; 3496 swipeIn->flags |= animationPlaceholder_DocumentWidgetFlag;
3497 setId_Widget(as_Widget(swipeIn), "swipein"); 3497 setId_Widget(as_Widget(swipeIn), "swipein");
@@ -3531,7 +3531,7 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
3531 iWidget *swipeParent = swipeParent_DocumentWidget_(d); 3531 iWidget *swipeParent = swipeParent_DocumentWidget_(d);
3532 if (findChild_Widget(swipeParent, "swipeout")) { 3532 if (findChild_Widget(swipeParent, "swipeout")) {
3533 return iTrue; /* too fast, previous animation hasn't finished */ 3533 return iTrue; /* too fast, previous animation hasn't finished */
3534 } 3534 }
3535 /* Setup the drag. `d` will be moving with the finger. */ 3535 /* Setup the drag. `d` will be moving with the finger. */
3536 animSpan = 0; 3536 animSpan = 0;
3537 postCommand_Widget(d, "navigate.forward"); 3537 postCommand_Widget(d, "navigate.forward");
@@ -3694,7 +3694,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
3694 else if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed") || 3694 else if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed") ||
3695 equal_Command(cmd, "keyroot.changed")) { 3695 equal_Command(cmd, "keyroot.changed")) {
3696 if (equal_Command(cmd, "font.changed")) { 3696 if (equal_Command(cmd, "font.changed")) {
3697 invalidateCachedLayout_History(d->mod.history); 3697 invalidateCachedLayout_History(d->mod.history);
3698 } 3698 }
3699 /* Alt/Option key may be involved in window size changes. */ 3699 /* Alt/Option key may be involved in window size changes. */
3700 setLinkNumberMode_DocumentWidget_(d, iFalse); 3700 setLinkNumberMode_DocumentWidget_(d, iFalse);
@@ -4056,7 +4056,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
4056 } 4056 }
4057 return wasHandled; 4057 return wasHandled;
4058 } 4058 }
4059 else if (equal_Command(cmd, "document.upload") && d == document_App()) { 4059 else if (equal_Command(cmd, "document.upload") && d == document_App()) {
4060 if (findChild_Widget(root_Widget(w), "upload")) { 4060 if (findChild_Widget(root_Widget(w), "upload")) {
4061 return iTrue; /* already open */ 4061 return iTrue; /* already open */
4062 } 4062 }
@@ -4124,7 +4124,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
4124 if (equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")) { 4124 if (equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")) {
4125 /* Reopen so the Upload dialog gets shown. */ 4125 /* Reopen so the Upload dialog gets shown. */
4126 postCommandf_App("open url:%s", cstr_String(d->mod.url)); 4126 postCommandf_App("open url:%s", cstr_String(d->mod.url));
4127 return iTrue; 4127 return iTrue;
4128 } 4128 }
4129 fetch_DocumentWidget_(d); 4129 fetch_DocumentWidget_(d);
4130 return iTrue; 4130 return iTrue;
@@ -4416,7 +4416,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
4416 if (argLabel_Command(cmd, "ttf")) { 4416 if (argLabel_Command(cmd, "ttf")) {
4417 iAssert(!cmp_String(&d->sourceMime, "font/ttf")); 4417 iAssert(!cmp_String(&d->sourceMime, "font/ttf"));
4418 installFontFile_Fonts(collect_String(suffix_Command(cmd, "name")), &d->sourceContent); 4418 installFontFile_Fonts(collect_String(suffix_Command(cmd, "name")), &d->sourceContent);
4419 postCommand_App("open url:about:fonts"); 4419 postCommand_App("open url:about:fonts");
4420 } 4420 }
4421 else { 4421 else {
4422 const iString *id = idFromUrl_FontPack(d->mod.url); 4422 const iString *id = idFromUrl_FontPack(d->mod.url);
@@ -5435,7 +5435,7 @@ void init_DocumentWidget(iDocumentWidget *d) {
5435 init_Widget(w); 5435 init_Widget(w);
5436 setId_Widget(w, format_CStr("document%03d", ++docEnum_)); 5436 setId_Widget(w, format_CStr("document%03d", ++docEnum_));
5437 setFlags_Widget(w, hover_WidgetFlag | noBackground_WidgetFlag, iTrue); 5437 setFlags_Widget(w, hover_WidgetFlag | noBackground_WidgetFlag, iTrue);
5438#if defined (iPlatformAppleDesktop) 5438#if defined (iPlatformAppleDesktop)
5439 iBool enableSwipeNavigation = iTrue; /* swipes on the trackpad */ 5439 iBool enableSwipeNavigation = iTrue; /* swipes on the trackpad */
5440#else 5440#else
5441 iBool enableSwipeNavigation = (deviceType_App() != desktop_AppDeviceType); 5441 iBool enableSwipeNavigation = (deviceType_App() != desktop_AppDeviceType);
@@ -5671,7 +5671,7 @@ iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) {
5671void takeRequest_DocumentWidget(iDocumentWidget *d, iGmRequest *finishedRequest) { 5671void takeRequest_DocumentWidget(iDocumentWidget *d, iGmRequest *finishedRequest) {
5672 cancelRequest_DocumentWidget_(d, iFalse /* don't post anything */); 5672 cancelRequest_DocumentWidget_(d, iFalse /* don't post anything */);
5673 const iString *url = url_GmRequest(finishedRequest); 5673 const iString *url = url_GmRequest(finishedRequest);
5674 5674
5675 add_History(d->mod.history, url); 5675 add_History(d->mod.history, url);
5676 setUrl_DocumentWidget_(d, url); 5676 setUrl_DocumentWidget_(d, url);
5677 d->state = fetching_RequestState; 5677 d->state = fetching_RequestState;
diff --git a/src/ui/touch.c b/src/ui/touch.c
index 0749bc7c..3d318dfb 100644
--- a/src/ui/touch.c
+++ b/src/ui/touch.c
@@ -42,7 +42,11 @@ iDeclareType(TouchState)
42 42
43static const uint32_t longPressSpanMs_ = 500; 43static const uint32_t longPressSpanMs_ = 500;
44static const uint32_t shortPressSpanMs_ = 250; 44static const uint32_t shortPressSpanMs_ = 250;
45#if defined (iPlatformAndroidMobile)
46static const int tapRadiusPt_ = 30; /* inaccurate sensors? */
47#else
45static const int tapRadiusPt_ = 10; 48static const int tapRadiusPt_ = 10;
49#endif
46 50
47enum iTouchEdge { 51enum iTouchEdge {
48 none_TouchEdge, 52 none_TouchEdge,
diff --git a/src/ui/window.c b/src/ui/window.c
index 0e13a57f..7f3371c8 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -263,6 +263,10 @@ static float pixelRatio_Window_(const iWindow *d) {
263# define baseDPI_Window 96.0f 263# define baseDPI_Window 96.0f
264#endif 264#endif
265 265
266#if defined (iPlatformAndroidMobile)
267float displayDensity_Android(void);
268#endif
269
266static float displayScale_Window_(const iWindow *d) { 270static float displayScale_Window_(const iWindow *d) {
267 /* The environment variable LAGRANGE_OVERRIDE_DPI can be used to override the automatic 271 /* The environment variable LAGRANGE_OVERRIDE_DPI can be used to override the automatic
268 display DPI detection. If not set, or is an empty string, ignore it. 272 display DPI detection. If not set, or is an empty string, ignore it.
@@ -289,6 +293,8 @@ static float displayScale_Window_(const iWindow *d) {
289#elif defined (iPlatformMsys) 293#elif defined (iPlatformMsys)
290 iUnused(d); 294 iUnused(d);
291 return desktopDPI_Win32(); 295 return desktopDPI_Win32();
296#elif defined (iPlatformAndroidMobile)
297 return displayDensity_Android();
292#else 298#else
293 if (isRunningUnderWindowSystem_App()) { 299 if (isRunningUnderWindowSystem_App()) {
294 float vdpi = 0.0f; 300 float vdpi = 0.0f;
@@ -457,7 +463,7 @@ void init_Window(iWindow *d, enum iWindowType type, iRect rect, uint32_t flags)
457 d->mouseGrab = NULL; 463 d->mouseGrab = NULL;
458 d->focus = NULL; 464 d->focus = NULL;
459 d->pendingCursor = NULL; 465 d->pendingCursor = NULL;
460 d->isExposed = iFalse; 466 d->isExposed = (deviceType_App() != desktop_AppDeviceType);
461 d->isMinimized = iFalse; 467 d->isMinimized = iFalse;
462 d->isInvalidated = iFalse; /* set when posting event, to avoid repeated events */ 468 d->isInvalidated = iFalse; /* set when posting event, to avoid repeated events */
463 d->isMouseInside = iTrue; 469 d->isMouseInside = iTrue;
@@ -541,6 +547,8 @@ void init_MainWindow(iMainWindow *d, iRect rect) {
541 SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); 547 SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal");
542 flags |= SDL_WINDOW_METAL; 548 flags |= SDL_WINDOW_METAL;
543 d->base.isExposed = iTrue; 549 d->base.isExposed = iTrue;
550#elif defined (iPlatformAndroidMobile)
551 d->base.isExposed = iTrue;
544#else 552#else
545 if (!forceSoftwareRender_App()) { 553 if (!forceSoftwareRender_App()) {
546 flags |= SDL_WINDOW_OPENGL; 554 flags |= SDL_WINDOW_OPENGL;