aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPo Lu2023-01-17 22:10:43 +0800
committerPo Lu2023-01-17 22:10:43 +0800
commit1b8258a1f2b6a080a4f0e819aa4a86c1ec2da89f (patch)
treed6c709e513882f5d430a98508e631cc503469fab /src
parent356249d9faf2b454879ff30f06d97beb97fb9a36 (diff)
downloademacs-1b8258a1f2b6a080a4f0e819aa4a86c1ec2da89f.tar.gz
emacs-1b8258a1f2b6a080a4f0e819aa4a86c1ec2da89f.zip
Update Android port
* doc/emacs/android.texi (Android Fonts): Document that TTC format fonts are now supported. * doc/emacs/emacs.texi (Top): Fix menus. * doc/lispref/commands.texi (Touchscreen Events) (Key Sequence Input): Document changes to touchscreen events. * etc/DEBUG: Describe how to debug 64 bit binaries on Android. * java/org/gnu/emacs/EmacsCopyArea.java (perform): Explicitly recycle copy bitmap. * java/org/gnu/emacs/EmacsDialog.java (EmacsDialog): New class. * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Use 5 point PolyLine like X, because Android behaves like Postscript on some devices and X elsewhere. * java/org/gnu/emacs/EmacsFillRectangle.java (perform): Explicitly recycle copy bitmap. * java/org/gnu/emacs/EmacsPixmap.java (destroyHandle): Explicitly recycle bitmap and GC if it is big. * java/org/gnu/emacs/EmacsView.java (EmacsView): Make `bitmapDirty' a boolean. (handleDirtyBitmap): Reimplement in terms of that boolean. Explicitly recycle old bitmap and GC. (onLayout): Fix lock up. (onDetachedFromWindow): Recycle bitmap and GC. * java/org/gnu/emacs/EmacsWindow.java (requestViewLayout): Update call to explicitlyDirtyBitmap. * src/android.c (android_run_select_thread, android_select): Really fix android_select. (android_build_jstring): New function. * src/android.h: Update prototypes. * src/androidmenu.c (android_process_events_for_menu): Totally unblock input before process_pending_signals. (android_menu_show): Remove redundant unblock_input and debugging code. (struct android_emacs_dialog, android_init_emacs_dialog) (android_dialog_show, android_popup_dialog, init_androidmenu): Implement popup dialogs on Android. * src/androidterm.c (android_update_tools) (handle_one_android_event, android_frame_up_to_date): Allow tapping tool bar items. (android_create_terminal): Add dialog hook. (android_wait_for_event): Adjust call to android_select. * src/androidterm.h (struct android_touch_point): New field `tool_bar_p'. * src/keyboard.c (read_key_sequence, head_table) (syms_of_keyboard): Prefix touchscreen events with posn. * src/keyboard.h (EVENT_HEAD): Handle touchscreen events. * src/process.c (wait_reading_process_output): Adjust call to android_select. * src/sfnt.c (sfnt_read_table_directory): If the first long turns out to be ttcf, return -1. (sfnt_read_ttc_header): New function. (main): Test TTC support. * src/sfnt.h (struct sfnt_ttc_header): New structure. (enum sfnt_ttc_tag): New enum. * src/sfntfont-android.c (struct sfntfont_android_scanline_buffer): New structure. (GET_SCANLINE_BUFFER): New macro. Try to avoid so much malloc upon accessing the scanline buffer. (sfntfont_android_put_glyphs): Do not use SAFE_ALLOCA to allocate the scaline buffer. (Fandroid_enumerate_fonts): Enumerate ttc fonts too. * src/sfntfont.c (struct sfnt_font_desc): New field `offset'. (sfnt_enum_font_1): Split out enumeration code from sfnt_enum_font. (sfnt_enum_font): Read TTC tables and enumerate each font therein. (sfntfont_open): Seek to the offset specified. * xcompile/Makefile.in (maintainer-clean): Fix depends here.
Diffstat (limited to 'src')
-rw-r--r--src/android.c62
-rw-r--r--src/android.h3
-rw-r--r--src/androidmenu.c240
-rw-r--r--src/androidterm.c100
-rw-r--r--src/androidterm.h7
-rw-r--r--src/keyboard.c7
-rw-r--r--src/keyboard.h13
-rw-r--r--src/process.c2
-rw-r--r--src/sfnt.c167
-rw-r--r--src/sfnt.h40
-rw-r--r--src/sfntfont-android.c99
-rw-r--r--src/sfntfont.c113
12 files changed, 771 insertions, 82 deletions
diff --git a/src/android.c b/src/android.c
index 9b15ea9f15a..cfb79045c0b 100644
--- a/src/android.c
+++ b/src/android.c
@@ -26,6 +26,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
26#include <semaphore.h> 26#include <semaphore.h>
27#include <dlfcn.h> 27#include <dlfcn.h>
28#include <errno.h> 28#include <errno.h>
29#include <math.h>
29 30
30#include <sys/stat.h> 31#include <sys/stat.h>
31#include <sys/mman.h> 32#include <sys/mman.h>
@@ -225,7 +226,6 @@ static fd_set *volatile android_pselect_readfds;
225static fd_set *volatile android_pselect_writefds; 226static fd_set *volatile android_pselect_writefds;
226static fd_set *volatile android_pselect_exceptfds; 227static fd_set *volatile android_pselect_exceptfds;
227static struct timespec *volatile android_pselect_timeout; 228static struct timespec *volatile android_pselect_timeout;
228static const sigset_t *volatile android_pselect_sigset;
229 229
230/* Value of pselect. */ 230/* Value of pselect. */
231static int android_pselect_rc; 231static int android_pselect_rc;
@@ -242,8 +242,8 @@ static sem_t android_pselect_sem, android_pselect_start_sem;
242static void * 242static void *
243android_run_select_thread (void *data) 243android_run_select_thread (void *data)
244{ 244{
245 sigset_t signals, sigset; 245 sigset_t signals, waitset;
246 int rc; 246 int rc, sig;
247 247
248 sigfillset (&signals); 248 sigfillset (&signals);
249 249
@@ -253,6 +253,8 @@ android_run_select_thread (void *data)
253 strerror (errno)); 253 strerror (errno));
254 254
255 sigdelset (&signals, SIGUSR1); 255 sigdelset (&signals, SIGUSR1);
256 sigemptyset (&waitset);
257 sigaddset (&waitset, SIGUSR1);
256 258
257 while (true) 259 while (true)
258 { 260 {
@@ -262,35 +264,33 @@ android_run_select_thread (void *data)
262 264
263 /* Get the select lock and call pselect. */ 265 /* Get the select lock and call pselect. */
264 pthread_mutex_lock (&event_queue.select_mutex); 266 pthread_mutex_lock (&event_queue.select_mutex);
265
266 /* Make sure SIGUSR1 can always wake pselect up. */
267 if (android_pselect_sigset)
268 {
269 sigset = *android_pselect_sigset;
270 sigdelset (&sigset, SIGUSR1);
271 android_pselect_sigset = &sigset;
272 }
273 else
274 android_pselect_sigset = &signals;
275
276 rc = pselect (android_pselect_nfds, 267 rc = pselect (android_pselect_nfds,
277 android_pselect_readfds, 268 android_pselect_readfds,
278 android_pselect_writefds, 269 android_pselect_writefds,
279 android_pselect_exceptfds, 270 android_pselect_exceptfds,
280 android_pselect_timeout, 271 android_pselect_timeout,
281 android_pselect_sigset); 272 &signals);
282 android_pselect_rc = rc; 273 android_pselect_rc = rc;
283 pthread_mutex_unlock (&event_queue.select_mutex); 274 pthread_mutex_unlock (&event_queue.select_mutex);
284 275
276 /* Signal the main thread that there is now data to read.
277 It is ok to signal this condition variable without holding
278 the event queue lock, because android_select will always
279 wait for this to complete before returning. */
280 android_pselect_completed = true;
281 pthread_cond_signal (&event_queue.read_var);
282
283 if (rc != -1 || errno != EINTR)
284 /* Now, wait for SIGUSR1, unless pselect was interrupted and
285 the signal was already delivered. The Emacs thread will
286 always send this signal after read_var is triggered or the
287 UI thread has sent an event. */
288 sigwait (&waitset, &sig);
289
285 /* Signal the Emacs thread that pselect is done. If read_var 290 /* Signal the Emacs thread that pselect is done. If read_var
286 was signaled by android_write_event, event_queue.mutex could 291 was signaled by android_write_event, event_queue.mutex could
287 still be locked, so this must come before. */ 292 still be locked, so this must come before. */
288 sem_post (&android_pselect_sem); 293 sem_post (&android_pselect_sem);
289
290 pthread_mutex_lock (&event_queue.mutex);
291 android_pselect_completed = true;
292 pthread_cond_signal (&event_queue.read_var);
293 pthread_mutex_unlock (&event_queue.mutex);
294 } 294 }
295} 295}
296 296
@@ -445,8 +445,7 @@ android_write_event (union android_event *event)
445 445
446int 446int
447android_select (int nfds, fd_set *readfds, fd_set *writefds, 447android_select (int nfds, fd_set *readfds, fd_set *writefds,
448 fd_set *exceptfds, struct timespec *timeout, 448 fd_set *exceptfds, struct timespec *timeout)
449 const sigset_t *sigset)
450{ 449{
451 int nfds_return; 450 int nfds_return;
452 451
@@ -467,7 +466,6 @@ android_select (int nfds, fd_set *readfds, fd_set *writefds,
467 android_pselect_writefds = writefds; 466 android_pselect_writefds = writefds;
468 android_pselect_exceptfds = exceptfds; 467 android_pselect_exceptfds = exceptfds;
469 android_pselect_timeout = timeout; 468 android_pselect_timeout = timeout;
470 android_pselect_sigset = sigset;
471 pthread_mutex_unlock (&event_queue.select_mutex); 469 pthread_mutex_unlock (&event_queue.select_mutex);
472 470
473 /* Release the select thread. */ 471 /* Release the select thread. */
@@ -3725,6 +3723,24 @@ android_build_string (Lisp_Object text)
3725 return string; 3723 return string;
3726} 3724}
3727 3725
3726/* Do the same, except TEXT is constant string data. */
3727
3728jstring
3729android_build_jstring (const char *text)
3730{
3731 jstring string;
3732
3733 string = (*android_java_env)->NewStringUTF (android_java_env,
3734 text);
3735 if (!string)
3736 {
3737 (*android_java_env)->ExceptionClear (android_java_env);
3738 memory_full (0);
3739 }
3740
3741 return string;
3742}
3743
3728/* Check for JNI exceptions and call memory_full in that 3744/* Check for JNI exceptions and call memory_full in that
3729 situation. */ 3745 situation. */
3730 3746
diff --git a/src/android.h b/src/android.h
index e68e0a51fbf..036e6d266fd 100644
--- a/src/android.h
+++ b/src/android.h
@@ -48,7 +48,7 @@ extern int ANDROID_EXPORT android_emacs_init (int, char **);
48#ifndef ANDROID_STUBIFY 48#ifndef ANDROID_STUBIFY
49 49
50extern int android_select (int, fd_set *, fd_set *, fd_set *, 50extern int android_select (int, fd_set *, fd_set *, fd_set *,
51 struct timespec *, const sigset_t *); 51 struct timespec *);
52 52
53extern bool android_file_access_p (const char *, int); 53extern bool android_file_access_p (const char *, int);
54extern int android_open (const char *, int, int); 54extern int android_open (const char *, int, int);
@@ -86,6 +86,7 @@ extern void android_set_dont_focus_on_map (android_window, bool);
86extern void android_set_dont_accept_focus (android_window, bool); 86extern void android_set_dont_accept_focus (android_window, bool);
87 87
88extern jstring android_build_string (Lisp_Object); 88extern jstring android_build_string (Lisp_Object);
89extern jstring android_build_jstring (const char *);
89extern void android_exception_check (void); 90extern void android_exception_check (void);
90 91
91extern void android_get_keysym_name (int, char *, size_t); 92extern void android_get_keysym_name (int, char *, size_t);
diff --git a/src/androidmenu.c b/src/androidmenu.c
index 6fb4963174b..f65b5d3ffd1 100644
--- a/src/androidmenu.c
+++ b/src/androidmenu.c
@@ -168,11 +168,17 @@ android_dismiss_menu (void *pointer)
168static void 168static void
169android_process_events_for_menu (int *id) 169android_process_events_for_menu (int *id)
170{ 170{
171 int blocked;
172
171 /* Set menu_event_id to -1; handle_one_android_event will set it to 173 /* Set menu_event_id to -1; handle_one_android_event will set it to
172 the event ID upon receiving a context menu event. This can cause 174 the event ID upon receiving a context menu event. This can cause
173 a non-local exit. */ 175 a non-local exit. */
174 x_display_list->menu_event_id = -1; 176 x_display_list->menu_event_id = -1;
175 177
178 /* Unblock input completely. */
179 blocked = interrupt_input_blocked;
180 totally_unblock_input ();
181
176 /* Now wait for the menu event ID to change. */ 182 /* Now wait for the menu event ID to change. */
177 while (x_display_list->menu_event_id == -1) 183 while (x_display_list->menu_event_id == -1)
178 { 184 {
@@ -181,11 +187,11 @@ android_process_events_for_menu (int *id)
181 187
182 /* Process pending signals. */ 188 /* Process pending signals. */
183 process_pending_signals (); 189 process_pending_signals ();
184
185 /* Maybe quit. */
186 maybe_quit ();
187 } 190 }
188 191
192 /* Restore the input block. */
193 interrupt_input_blocked = blocked;
194
189 /* Return the ID. */ 195 /* Return the ID. */
190 *id = x_display_list->menu_event_id; 196 *id = x_display_list->menu_event_id;
191} 197}
@@ -420,9 +426,7 @@ android_menu_show (struct frame *f, int x, int y, int menuflags,
420 426
421 /* Next, process events waiting for something to be selected. */ 427 /* Next, process events waiting for something to be selected. */
422 popup_activated_flag = 1; 428 popup_activated_flag = 1;
423 unblock_input ();
424 android_process_events_for_menu (&id); 429 android_process_events_for_menu (&id);
425 block_input ();
426 430
427 if (!id) 431 if (!id)
428 /* This means no menu item was selected. */ 432 /* This means no menu item was selected. */
@@ -498,8 +502,6 @@ android_menu_show (struct frame *f, int x, int y, int menuflags,
498 } 502 }
499 } 503 }
500 504
501 Fprint (tem, Qexternal_debugging_output);
502
503 unblock_input (); 505 unblock_input ();
504 return unbind_to (count, tem); 506 return unbind_to (count, tem);
505 507
@@ -508,6 +510,229 @@ android_menu_show (struct frame *f, int x, int y, int menuflags,
508 return unbind_to (count, Qnil); 510 return unbind_to (count, Qnil);
509} 511}
510 512
513
514
515/* Toolkit dialog implementation. */
516
517/* Structure describing the EmacsDialog class. */
518
519struct android_emacs_dialog
520{
521 jclass class;
522 jmethodID create_dialog;
523 jmethodID add_button;
524 jmethodID display;
525};
526
527/* Identifiers associated with the EmacsDialog class. */
528static struct android_emacs_dialog dialog_class;
529
530static void
531android_init_emacs_dialog (void)
532{
533 jclass old;
534
535 dialog_class.class
536 = (*android_java_env)->FindClass (android_java_env,
537 "org/gnu/emacs/EmacsDialog");
538 eassert (dialog_class.class);
539
540 old = dialog_class.class;
541 dialog_class.class
542 = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
543 (jobject) old);
544 ANDROID_DELETE_LOCAL_REF (old);
545
546 if (!dialog_class.class)
547 emacs_abort ();
548
549#define FIND_METHOD(c_name, name, signature) \
550 dialog_class.c_name \
551 = (*android_java_env)->GetMethodID (android_java_env, \
552 dialog_class.class, \
553 name, signature); \
554 eassert (dialog_class.c_name);
555
556#define FIND_METHOD_STATIC(c_name, name, signature) \
557 dialog_class.c_name \
558 = (*android_java_env)->GetStaticMethodID (android_java_env, \
559 dialog_class.class, \
560 name, signature); \
561
562 FIND_METHOD_STATIC (create_dialog, "createDialog", "(Ljava/lang/String;"
563 "Ljava/lang/String;)Lorg/gnu/emacs/EmacsDialog;");
564 FIND_METHOD (add_button, "addButton", "(Ljava/lang/String;IZ)V");
565 FIND_METHOD (display, "display", "()Z");
566
567#undef FIND_METHOD
568#undef FIND_METHOD_STATIC
569}
570
571static Lisp_Object
572android_dialog_show (struct frame *f, Lisp_Object title,
573 Lisp_Object header, const char **error_name)
574{
575 specpdl_ref count;
576 jobject dialog, java_header, java_title, temp;
577 size_t i;
578 Lisp_Object item_name, enable, entry;
579 bool rc;
580 int id;
581 jmethodID method;
582
583 if (menu_items_n_panes > 1)
584 {
585 *error_name = "Multiple panes in dialog box";
586 return Qnil;
587 }
588
589 /* Do the initial setup. */
590 count = SPECPDL_INDEX ();
591 *error_name = NULL;
592
593 android_push_local_frame ();
594
595 /* Figure out what header to use. */
596 java_header = (!NILP (header)
597 ? android_build_jstring ("Information")
598 : android_build_jstring ("Question"));
599
600 /* And the title. */
601 java_title = android_build_string (title);
602
603 /* Now create the dialog. */
604 method = dialog_class.create_dialog;
605 dialog = (*android_java_env)->CallStaticObjectMethod (android_java_env,
606 dialog_class.class,
607 method, java_header,
608 java_title);
609 android_exception_check ();
610
611 /* Delete now unused local references. */
612 if (java_header)
613 ANDROID_DELETE_LOCAL_REF (java_header);
614 ANDROID_DELETE_LOCAL_REF (java_title);
615
616 /* Create the buttons. */
617 i = MENU_ITEMS_PANE_LENGTH;
618 while (i < menu_items_used)
619 {
620 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
621 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
622
623 /* Verify that there is no submenu here. */
624
625 if (NILP (item_name))
626 {
627 *error_name = "Submenu in dialog items";
628 return unbind_to (count, Qnil);
629 }
630
631 /* Skip past boundaries between buttons on different sides. The
632 Android toolkit is too silly to understand this
633 distinction. */
634
635 if (EQ (item_name, Qquote))
636 ++i;
637 else
638 {
639 /* Make sure i is within bounds. */
640 if (i > TYPE_MAXIMUM (jint))
641 {
642 *error_name = "Dialog box too big";
643 return unbind_to (count, Qnil);
644 }
645
646 /* Add the button. */
647 temp = android_build_string (item_name);
648 (*android_java_env)->CallVoidMethod (android_java_env,
649 dialog,
650 dialog_class.add_button,
651 temp, (jint) i,
652 (jboolean) NILP (enable));
653 android_exception_check ();
654 ANDROID_DELETE_LOCAL_REF (temp);
655 i += MENU_ITEMS_ITEM_LENGTH;
656 }
657 }
658
659 /* The dialog is now built. Run it. */
660 rc = (*android_java_env)->CallBooleanMethod (android_java_env,
661 dialog,
662 dialog_class.display);
663 android_exception_check ();
664
665 if (!rc)
666 quit ();
667
668 /* Wait for the menu ID to arrive. */
669 android_process_events_for_menu (&id);
670
671 if (!id)
672 quit ();
673
674 /* Find the selected item, and its pane, to return
675 the proper value. */
676 i = 0;
677 while (i < menu_items_used)
678 {
679 if (EQ (AREF (menu_items, i), Qt))
680 i += MENU_ITEMS_PANE_LENGTH;
681 else if (EQ (AREF (menu_items, i), Qquote))
682 /* This is the boundary between left-side elts and right-side
683 elts. */
684 ++i;
685 else
686 {
687 entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
688
689 if (id == i)
690 return entry;
691
692 i += MENU_ITEMS_ITEM_LENGTH;
693 }
694 }
695
696 return Qnil;
697}
698
699Lisp_Object
700android_popup_dialog (struct frame *f, Lisp_Object header,
701 Lisp_Object contents)
702{
703 Lisp_Object title;
704 const char *error_name;
705 Lisp_Object selection;
706 specpdl_ref specpdl_count = SPECPDL_INDEX ();
707
708 check_window_system (f);
709
710 /* Decode the dialog items from what was specified. */
711 title = Fcar (contents);
712 CHECK_STRING (title);
713 record_unwind_protect_void (unuse_menu_items);
714
715 if (NILP (Fcar (Fcdr (contents))))
716 /* No buttons specified, add an "Ok" button so users can pop down
717 the dialog. */
718 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
719
720 list_of_panes (list1 (contents));
721
722 /* Display them in a dialog box. */
723 block_input ();
724 selection = android_dialog_show (f, title, header, &error_name);
725 unblock_input ();
726
727 unbind_to (specpdl_count, Qnil);
728 discard_menu_items ();
729
730 if (error_name)
731 error ("%s", error_name);
732
733 return selection;
734}
735
511#else 736#else
512 737
513int 738int
@@ -531,6 +756,7 @@ init_androidmenu (void)
531{ 756{
532#ifndef ANDROID_STUBIFY 757#ifndef ANDROID_STUBIFY
533 android_init_emacs_context_menu (); 758 android_init_emacs_context_menu ();
759 android_init_emacs_dialog ();
534#endif 760#endif
535} 761}
536 762
diff --git a/src/androidterm.c b/src/androidterm.c
index cc2da279bb3..f19cee5b11b 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -496,10 +496,17 @@ android_update_tools (struct frame *f, struct input_event *ie)
496 /* Build the list of active touches. */ 496 /* Build the list of active touches. */
497 for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points; 497 for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points;
498 touchpoint; touchpoint = touchpoint->next) 498 touchpoint; touchpoint = touchpoint->next)
499 ie->arg = Fcons (list3i (touchpoint->x, 499 {
500 touchpoint->y, 500 /* Skip touch points which originated on the tool bar. */
501 touchpoint->tool_id), 501
502 ie->arg); 502 if (touchpoint->tool_bar_p)
503 continue;
504
505 ie->arg = Fcons (list3i (touchpoint->x,
506 touchpoint->y,
507 touchpoint->tool_id),
508 ie->arg);
509 }
503} 510}
504 511
505/* Find and return an existing tool pressed against FRAME, identified 512/* Find and return an existing tool pressed against FRAME, identified
@@ -951,6 +958,59 @@ handle_one_android_event (struct android_display_info *dpyinfo,
951 touchpoint->next = FRAME_OUTPUT_DATA (any)->touch_points; 958 touchpoint->next = FRAME_OUTPUT_DATA (any)->touch_points;
952 FRAME_OUTPUT_DATA (any)->touch_points = touchpoint; 959 FRAME_OUTPUT_DATA (any)->touch_points = touchpoint;
953 960
961 /* Figure out whether or not the tool was pressed on the tool
962 bar. Note that the code which runs when it was is more or
963 less an abuse of the mouse highlight machinery, but it works
964 well enough in practice. */
965
966 if (WINDOWP (any->tool_bar_window)
967 && WINDOW_TOTAL_LINES (XWINDOW (any->tool_bar_window)))
968 {
969 Lisp_Object window;
970 int x = event->touch.x;
971 int y = event->touch.y;
972
973 window = window_from_coordinates (any, x, y, 0, true,
974 true);
975
976 /* If this touch has started in the tool bar, do not
977 send it to Lisp. Instead, simulate a tool bar
978 click, releasing it once it goes away. */
979
980 if (EQ (window, any->tool_bar_window))
981 {
982 /* Call note_mouse_highlight on the tool bar
983 item. Otherwise, get_tool_bar_item will
984 return 1.
985
986 This is not necessary when mouse-highlight is
987 nil. */
988
989 if (!NILP (Vmouse_highlight))
990 {
991 note_mouse_highlight (any, x, y);
992
993 /* Always allow future mouse motion to
994 update the mouse highlight, no matter
995 where it is. */
996 memset (&dpyinfo->last_mouse_glyph, 0,
997 sizeof dpyinfo->last_mouse_glyph);
998 dpyinfo->last_mouse_glyph_frame = any;
999 }
1000
1001 handle_tool_bar_click (any, x, y, true, 0);
1002
1003 /* Flush any changes made by that to the front
1004 buffer. */
1005 android_flush_dirty_back_buffer_on (any);
1006
1007 /* Mark the touch point as being grabbed by the tool
1008 bar. */
1009 touchpoint->tool_bar_p = true;
1010 goto OTHER;
1011 }
1012 }
1013
954 /* Now generate the Emacs event. */ 1014 /* Now generate the Emacs event. */
955 inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT; 1015 inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT;
956 inev.ie.timestamp = event->touch.time; 1016 inev.ie.timestamp = event->touch.time;
@@ -970,9 +1030,10 @@ handle_one_android_event (struct android_display_info *dpyinfo,
970 1030
971 touchpoint = android_find_tool (any, event->touch.pointer_id); 1031 touchpoint = android_find_tool (any, event->touch.pointer_id);
972 1032
973 /* If it doesn't exist, skip processing this event. */ 1033 /* If it doesn't exist or has been grabbed by the tool bar, skip
1034 processing this event. */
974 1035
975 if (!touchpoint) 1036 if (!touchpoint || touchpoint->tool_bar_p)
976 goto OTHER; 1037 goto OTHER;
977 1038
978 /* Otherwise, update the position and send the update event. */ 1039 /* Otherwise, update the position and send the update event. */
@@ -999,8 +1060,27 @@ handle_one_android_event (struct android_display_info *dpyinfo,
999 *last = touchpoint->next; 1060 *last = touchpoint->next;
1000 1061
1001 /* The tool was unlinked. Free it and generate the 1062 /* The tool was unlinked. Free it and generate the
1002 appropriate Emacs event. */ 1063 appropriate Emacs event (assuming that it was not
1064 grabbed by the tool bar). */
1003 xfree (touchpoint); 1065 xfree (touchpoint);
1066
1067 if (touchpoint->tool_bar_p)
1068 {
1069 /* Do what is necessary to release the tool bar and
1070 possibly trigger a click. */
1071
1072 if (any->last_tool_bar_item != -1)
1073 handle_tool_bar_click (any, event->touch.x,
1074 event->touch.y, false,
1075 0);
1076
1077 /* Cancel any outstanding mouse highlight. */
1078 note_mouse_highlight (any, -1, -1);
1079 android_flush_dirty_back_buffer_on (any);
1080
1081 goto OTHER;
1082 }
1083
1004 inev.ie.kind = TOUCHSCREEN_END_EVENT; 1084 inev.ie.kind = TOUCHSCREEN_END_EVENT;
1005 inev.ie.timestamp = event->touch.time; 1085 inev.ie.timestamp = event->touch.time;
1006 1086
@@ -1227,6 +1307,9 @@ android_frame_up_to_date (struct frame *f)
1227 1307
1228 /* The frame is now complete, as its contents have been drawn. */ 1308 /* The frame is now complete, as its contents have been drawn. */
1229 FRAME_ANDROID_COMPLETE_P (f) = true; 1309 FRAME_ANDROID_COMPLETE_P (f) = true;
1310
1311 /* Shrink the scanline buffer used by the font backend. */
1312 sfntfont_android_shrink_scanline_buffer ();
1230 unblock_input (); 1313 unblock_input ();
1231} 1314}
1232 1315
@@ -1513,7 +1596,7 @@ android_wait_for_event (struct frame *f, int eventtype)
1513 break; 1596 break;
1514 1597
1515 tmo = timespec_sub (tmo_at, time_now); 1598 tmo = timespec_sub (tmo_at, time_now);
1516 if (android_select (0, NULL, NULL, NULL, &tmo, NULL) == 0) 1599 if (android_select (0, NULL, NULL, NULL, &tmo) == 0)
1517 break; /* Timeout */ 1600 break; /* Timeout */
1518 } 1601 }
1519 1602
@@ -4061,6 +4144,7 @@ android_create_terminal (struct android_display_info *dpyinfo)
4061 terminal->set_bitmap_icon_hook = android_bitmap_icon; 4144 terminal->set_bitmap_icon_hook = android_bitmap_icon;
4062 terminal->implicit_set_name_hook = android_implicitly_set_name; 4145 terminal->implicit_set_name_hook = android_implicitly_set_name;
4063 terminal->menu_show_hook = android_menu_show; 4146 terminal->menu_show_hook = android_menu_show;
4147 terminal->popup_dialog_hook = android_popup_dialog;
4064 terminal->change_tab_bar_height_hook = android_change_tab_bar_height; 4148 terminal->change_tab_bar_height_hook = android_change_tab_bar_height;
4065 terminal->change_tool_bar_height_hook = android_change_tool_bar_height; 4149 terminal->change_tool_bar_height_hook = android_change_tool_bar_height;
4066 terminal->set_scroll_bar_default_width_hook 4150 terminal->set_scroll_bar_default_width_hook
diff --git a/src/androidterm.h b/src/androidterm.h
index 9aa09877196..c0f862e35fb 100644
--- a/src/androidterm.h
+++ b/src/androidterm.h
@@ -148,6 +148,9 @@ struct android_touch_point
148 148
149 /* The tool ID and the last known X and Y positions. */ 149 /* The tool ID and the last known X and Y positions. */
150 int tool_id, x, y; 150 int tool_id, x, y;
151
152 /* Whether or not the tool is pressed on the tool bar. */
153 bool tool_bar_p;
151}; 154};
152 155
153struct android_output 156struct android_output
@@ -410,6 +413,9 @@ extern void android_finalize_font_entity (struct font_entity *);
410 413
411extern Lisp_Object android_menu_show (struct frame *, int, int, int, 414extern Lisp_Object android_menu_show (struct frame *, int, int, int,
412 Lisp_Object, const char **); 415 Lisp_Object, const char **);
416extern Lisp_Object android_popup_dialog (struct frame *, Lisp_Object,
417 Lisp_Object);
418
413extern void init_androidmenu (void); 419extern void init_androidmenu (void);
414extern void syms_of_androidmenu (void); 420extern void syms_of_androidmenu (void);
415 421
@@ -417,6 +423,7 @@ extern void syms_of_androidmenu (void);
417 423
418extern const struct font_driver android_sfntfont_driver; 424extern const struct font_driver android_sfntfont_driver;
419 425
426extern void sfntfont_android_shrink_scanline_buffer (void);
420extern void init_sfntfont_android (void); 427extern void init_sfntfont_android (void);
421extern void syms_of_sfntfont_android (void); 428extern void syms_of_sfntfont_android (void);
422 429
diff --git a/src/keyboard.c b/src/keyboard.c
index 990b5307f14..834049b496a 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -10380,7 +10380,7 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt,
10380 if (EVENT_HAS_PARAMETERS (key)) 10380 if (EVENT_HAS_PARAMETERS (key))
10381 { 10381 {
10382 Lisp_Object kind = EVENT_HEAD_KIND (EVENT_HEAD (key)); 10382 Lisp_Object kind = EVENT_HEAD_KIND (EVENT_HEAD (key));
10383 if (EQ (kind, Qmouse_click)) 10383 if (EQ (kind, Qmouse_click) || EQ (kind, Qtouchscreen))
10384 { 10384 {
10385 Lisp_Object window = POSN_WINDOW (EVENT_START (key)); 10385 Lisp_Object window = POSN_WINDOW (EVENT_START (key));
10386 Lisp_Object posn = POSN_POSN (EVENT_START (key)); 10386 Lisp_Object posn = POSN_POSN (EVENT_START (key));
@@ -12185,7 +12185,9 @@ static const struct event_head head_table[] = {
12185 {SYMBOL_INDEX (Qmake_frame_visible), SYMBOL_INDEX (Qmake_frame_visible)}, 12185 {SYMBOL_INDEX (Qmake_frame_visible), SYMBOL_INDEX (Qmake_frame_visible)},
12186 /* `select-window' should be handled just like `switch-frame' 12186 /* `select-window' should be handled just like `switch-frame'
12187 in read_key_sequence. */ 12187 in read_key_sequence. */
12188 {SYMBOL_INDEX (Qselect_window), SYMBOL_INDEX (Qswitch_frame)} 12188 {SYMBOL_INDEX (Qselect_window), SYMBOL_INDEX (Qswitch_frame)},
12189 /* Touchscreen events should be prefixed by the posn. */
12190 {SYMBOL_INDEX (Qtouchscreen_begin), SYMBOL_INDEX (Qtouchscreen)},
12189}; 12191};
12190 12192
12191static Lisp_Object 12193static Lisp_Object
@@ -12895,6 +12897,7 @@ See also `pre-command-hook'. */);
12895 "display-monitors-changed-functions"); 12897 "display-monitors-changed-functions");
12896 12898
12897 DEFSYM (Qcoding, "coding"); 12899 DEFSYM (Qcoding, "coding");
12900 DEFSYM (Qtouchscreen, "touchscreen");
12898 12901
12899 Fset (Qecho_area_clear_hook, Qnil); 12902 Fset (Qecho_area_clear_hook, Qnil);
12900 12903
diff --git a/src/keyboard.h b/src/keyboard.h
index 3f86a8e03ad..26eecd48b00 100644
--- a/src/keyboard.h
+++ b/src/keyboard.h
@@ -395,8 +395,17 @@ extern void unuse_menu_items (void);
395#define EVENT_HEAD(event) \ 395#define EVENT_HEAD(event) \
396 (EVENT_HAS_PARAMETERS (event) ? XCAR (event) : (event)) 396 (EVENT_HAS_PARAMETERS (event) ? XCAR (event) : (event))
397 397
398/* Extract the starting and ending positions from a composite event. */ 398/* Extract the starting and ending positions from a composite event. */
399#define EVENT_START(event) (CAR_SAFE (CDR_SAFE (event))) 399
400/* Unlike Lisp `event-start', this also handles touch screen events,
401 which are not actually mouse events in the general sense. */
402#define EVENT_START(event) \
403 ((EQ (EVENT_HEAD (event), Qtouchscreen_begin) \
404 || EQ (EVENT_HEAD (event), Qtouchscreen_end)) \
405 ? CDR_SAFE (CAR_SAFE (CDR_SAFE (event))) \
406 : CAR_SAFE (CDR_SAFE (event)))
407
408/* This does not handle touchscreen events. */
400#define EVENT_END(event) (CAR_SAFE (CDR_SAFE (CDR_SAFE (event)))) 409#define EVENT_END(event) (CAR_SAFE (CDR_SAFE (CDR_SAFE (event))))
401 410
402/* Extract the click count from a multi-click event. */ 411/* Extract the click count from a multi-click event. */
diff --git a/src/process.c b/src/process.c
index 111e0c80e43..651b5fa035b 100644
--- a/src/process.c
+++ b/src/process.c
@@ -5689,7 +5689,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
5689#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY 5689#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
5690 nfds = android_select (max_desc + 1, 5690 nfds = android_select (max_desc + 1,
5691 &Available, (check_write ? &Writeok : 0), 5691 &Available, (check_write ? &Writeok : 0),
5692 NULL, &timeout, NULL); 5692 NULL, &timeout);
5693#else 5693#else
5694 5694
5695 /* Non-macOS HAVE_GLIB builds call thread_select in 5695 /* Non-macOS HAVE_GLIB builds call thread_select in
diff --git a/src/sfnt.c b/src/sfnt.c
index ee74ba0fefe..6d58798c599 100644
--- a/src/sfnt.c
+++ b/src/sfnt.c
@@ -129,8 +129,12 @@ _sfnt_swap32 (uint32_t *value)
129#define sfnt_swap32(what) (_sfnt_swap32 ((uint32_t *) (what))) 129#define sfnt_swap32(what) (_sfnt_swap32 ((uint32_t *) (what)))
130 130
131/* Read the table directory from the file FD. FD must currently be at 131/* Read the table directory from the file FD. FD must currently be at
132 the start of the file, and must be seekable. Return the table 132 the start of the file (or an offset defined in the TTC header, if
133 directory upon success, else NULL. */ 133 applicable), and must be seekable. Return the table directory upon
134 success, else NULL.
135
136 Value is NULL upon failure, and the offset subtable upon success.
137 If FD is actually a TrueType collection file, value is -1. */
134 138
135TEST_STATIC struct sfnt_offset_subtable * 139TEST_STATIC struct sfnt_offset_subtable *
136sfnt_read_table_directory (int fd) 140sfnt_read_table_directory (int fd)
@@ -147,11 +151,34 @@ sfnt_read_table_directory (int fd)
147 151
148 if (rc < offset) 152 if (rc < offset)
149 { 153 {
154 if (rc >= sizeof (uint32_t))
155 {
156 /* Detect a TTC file. In that case, the first long will be
157 ``ttcf''. */
158 sfnt_swap32 (&subtable->scaler_type);
159
160 if (subtable->scaler_type == SFNT_TTC_TTCF)
161 {
162 xfree (subtable);
163 return (struct sfnt_offset_subtable *) -1;
164 }
165 }
166
150 xfree (subtable); 167 xfree (subtable);
151 return NULL; 168 return NULL;
152 } 169 }
153 170
154 sfnt_swap32 (&subtable->scaler_type); 171 sfnt_swap32 (&subtable->scaler_type);
172
173 /* Bail out early if this font is actually a TrueType collection
174 file. */
175
176 if (subtable->scaler_type == SFNT_TTC_TTCF)
177 {
178 xfree (subtable);
179 return (struct sfnt_offset_subtable *) -1;
180 }
181
155 sfnt_swap16 (&subtable->num_tables); 182 sfnt_swap16 (&subtable->num_tables);
156 sfnt_swap16 (&subtable->search_range); 183 sfnt_swap16 (&subtable->search_range);
157 sfnt_swap16 (&subtable->entry_selector); 184 sfnt_swap16 (&subtable->entry_selector);
@@ -4183,6 +4210,101 @@ sfnt_find_metadata (struct sfnt_meta_table *meta,
4183 4210
4184 4211
4185 4212
4213/* TrueType collection format support. */
4214
4215/* Read a TrueType collection header from the font file FD.
4216 FD must currently at the start of the file.
4217
4218 Value is the header upon success, else NULL. */
4219
4220TEST_STATIC struct sfnt_ttc_header *
4221sfnt_read_ttc_header (int fd)
4222{
4223 struct sfnt_ttc_header *ttc;
4224 size_t size, i;
4225 ssize_t rc;
4226
4227 /* First, allocate only as much as required. */
4228
4229 ttc = xmalloc (sizeof *ttc);
4230
4231 /* Read the version 1.0 data. */
4232
4233 size = SFNT_ENDOF (struct sfnt_ttc_header, num_fonts,
4234 uint32_t);
4235 rc = read (fd, ttc, size);
4236 if (rc < size)
4237 {
4238 xfree (ttc);
4239 return NULL;
4240 }
4241
4242 /* Now swap what was read. */
4243 sfnt_swap32 (&ttc->ttctag);
4244 sfnt_swap32 (&ttc->version);
4245 sfnt_swap32 (&ttc->num_fonts);
4246
4247 /* Verify that the tag is as expected. */
4248 if (ttc->ttctag != SFNT_TTC_TTCF)
4249 {
4250 xfree (ttc);
4251 return NULL;
4252 }
4253
4254 /* Now, read the variable length data. Make sure to check for
4255 overflow. */
4256
4257 if (INT_MULTIPLY_WRAPV (ttc->num_fonts,
4258 sizeof *ttc->offset_table,
4259 &size))
4260 {
4261 xfree (ttc);
4262 return NULL;
4263 }
4264
4265 ttc = xrealloc (ttc, sizeof *ttc + size);
4266 ttc->offset_table = (uint32_t *) (ttc + 1);
4267 rc = read (fd, ttc->offset_table, size);
4268 if (rc < size)
4269 {
4270 xfree (ttc);
4271 return NULL;
4272 }
4273
4274 /* Swap each of the offsets read. */
4275 for (i = 0; i < ttc->num_fonts; ++i)
4276 sfnt_swap32 (&ttc->offset_table[i]);
4277
4278 /* Now, look at the version. If it is earlier than 2.0, then
4279 reading is finished. */
4280
4281 if (ttc->version < 0x00020000)
4282 return ttc;
4283
4284 /* If it is 2.0 or later, then continue to read ul_dsig_tag to
4285 ul_dsig_offset. */
4286
4287 size = (SFNT_ENDOF (struct sfnt_ttc_header, ul_dsig_offset,
4288 uint32_t)
4289 - offsetof (struct sfnt_ttc_header, ul_dsig_tag));
4290 rc = read (fd, &ttc->ul_dsig_offset, size);
4291 if (rc < size)
4292 {
4293 xfree (ttc);
4294 return NULL;
4295 }
4296
4297 /* Swap what was read. */
4298 sfnt_swap32 (&ttc->ul_dsig_tag);
4299 sfnt_swap32 (&ttc->ul_dsig_length);
4300 sfnt_swap32 (&ttc->ul_dsig_offset);
4301
4302 /* All done. */
4303 return ttc;
4304}
4305
4306
4307
4186#ifdef TEST 4308#ifdef TEST
4187 4309
4188struct sfnt_test_dcontext 4310struct sfnt_test_dcontext
@@ -4397,6 +4519,7 @@ main (int argc, char **argv)
4397 unsigned char *string; 4519 unsigned char *string;
4398 struct sfnt_name_record record; 4520 struct sfnt_name_record record;
4399 struct sfnt_meta_table *meta; 4521 struct sfnt_meta_table *meta;
4522 struct sfnt_ttc_header *ttc;
4400 4523
4401 if (argc != 2) 4524 if (argc != 2)
4402 return 1; 4525 return 1;
@@ -4406,8 +4529,41 @@ main (int argc, char **argv)
4406 if (fd < 1) 4529 if (fd < 1)
4407 return 1; 4530 return 1;
4408 4531
4532 ttc = NULL;
4533
4409 font = sfnt_read_table_directory (fd); 4534 font = sfnt_read_table_directory (fd);
4410 4535
4536 if (font == (struct sfnt_offset_subtable *) -1)
4537 {
4538 if (lseek (fd, 0, SEEK_SET) != 0)
4539 return 1;
4540
4541 ttc = sfnt_read_ttc_header (fd);
4542
4543 if (!ttc)
4544 return 1;
4545
4546 fprintf (stderr, "TrueType collection: %"PRIu32" fonts installed\n",
4547 ttc->num_fonts);
4548 fflush (stderr);
4549
4550 printf ("Which font? ");
4551 if (scanf ("%d", &i) == EOF)
4552 return 1;
4553
4554 if (i >= ttc->num_fonts || i < 0)
4555 {
4556 printf ("out of range\n");
4557 return 1;
4558 }
4559
4560 if (lseek (fd, ttc->offset_table[i], SEEK_SET)
4561 != ttc->offset_table[i])
4562 return 1;
4563
4564 font = sfnt_read_table_directory (fd);
4565 }
4566
4411 if (!font) 4567 if (!font)
4412 { 4568 {
4413 close (fd); 4569 close (fd);
@@ -4432,9 +4588,9 @@ main (int argc, char **argv)
4432 for (i = 0; i < table->num_subtables; ++i) 4588 for (i = 0; i < table->num_subtables; ++i)
4433 { 4589 {
4434 fprintf (stderr, "Found cmap table %"PRIu32": %p\n", 4590 fprintf (stderr, "Found cmap table %"PRIu32": %p\n",
4435 subtables[i].offset, data); 4591 subtables[i].offset, data[i]);
4436 4592
4437 if (data) 4593 if (data[i])
4438 fprintf (stderr, " format: %"PRIu16"\n", 4594 fprintf (stderr, " format: %"PRIu16"\n",
4439 data[i]->format); 4595 data[i]->format);
4440 } 4596 }
@@ -4552,7 +4708,7 @@ main (int argc, char **argv)
4552 if (scanf ("%d %"SCNu32"", &i, &character) == EOF) 4708 if (scanf ("%d %"SCNu32"", &i, &character) == EOF)
4553 break; 4709 break;
4554 4710
4555 if (i >= table->num_subtables) 4711 if (i < 0 || i >= table->num_subtables)
4556 { 4712 {
4557 printf ("table out of range\n"); 4713 printf ("table out of range\n");
4558 continue; 4714 continue;
@@ -4699,6 +4855,7 @@ main (int argc, char **argv)
4699 xfree (hmtx); 4855 xfree (hmtx);
4700 xfree (name); 4856 xfree (name);
4701 xfree (meta); 4857 xfree (meta);
4858 xfree (ttc);
4702 4859
4703 return 0; 4860 return 0;
4704} 4861}
diff --git a/src/sfnt.h b/src/sfnt.h
index 91d7b261cb0..fe6b6ec3dd7 100644
--- a/src/sfnt.h
+++ b/src/sfnt.h
@@ -875,6 +875,44 @@ enum sfnt_meta_data_tag
875 875
876 876
877 877
878/* TrueType collection format support. */
879
880struct sfnt_ttc_header
881{
882 /* TrueType collection ID tag. */
883 uint32_t ttctag;
884
885 /* Version of the TTC header. */
886 uint32_t version;
887
888 /* Number of fonts in the TTC header. */
889 uint32_t num_fonts;
890
891 /* Array of offsets to the offset table for each font in the
892 file. */
893 uint32_t *offset_table;
894
895 /* Tag indicating that a DSIG table exists, or 0. Fields from here
896 on are only set on version 2.0 headers or later. */
897 uint32_t ul_dsig_tag;
898
899 /* Length in bytes of the signature table, or 0 if there is no
900 signature. */
901 uint32_t ul_dsig_length;
902
903 /* Offset in bytes of the dsig table from the beginning of the TTC
904 file. */
905 uint32_t ul_dsig_offset;
906};
907
908enum sfnt_ttc_tag
909 {
910 SFNT_TTC_TTCF = 0x74746366,
911 SFNT_TTC_DSIG = 0x44534947,
912 };
913
914
915
878#define SFNT_CEIL_FIXED(fixed) \ 916#define SFNT_CEIL_FIXED(fixed) \
879 (!((fixed) & 0177777) ? (fixed) \ 917 (!((fixed) & 0177777) ? (fixed) \
880 : ((fixed) + 0200000) & 037777600000) 918 : ((fixed) + 0200000) & 037777600000)
@@ -960,5 +998,7 @@ extern char *sfnt_find_metadata (struct sfnt_meta_table *,
960 enum sfnt_meta_data_tag, 998 enum sfnt_meta_data_tag,
961 struct sfnt_meta_data_map *); 999 struct sfnt_meta_data_map *);
962 1000
1001extern struct sfnt_ttc_header *sfnt_read_ttc_header (int);
1002
963#endif /* TEST */ 1003#endif /* TEST */
964#endif /* _SFNT_H_ */ 1004#endif /* _SFNT_H_ */
diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c
index cddb3fd40f3..1b01a4d9be4 100644
--- a/src/sfntfont-android.c
+++ b/src/sfntfont-android.c
@@ -31,6 +31,17 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
31#include "blockinput.h" 31#include "blockinput.h"
32#include "android.h" 32#include "android.h"
33 33
34/* Structure describing a temporary buffer. */
35
36struct sfntfont_android_scanline_buffer
37{
38 /* Size of this buffer. */
39 size_t buffer_size;
40
41 /* Pointer to the buffer data. */
42 void *buffer_data;
43};
44
34/* Array of directories to search for system fonts. */ 45/* Array of directories to search for system fonts. */
35const char *system_font_directories[] = 46const char *system_font_directories[] =
36 { 47 {
@@ -40,6 +51,49 @@ const char *system_font_directories[] =
40/* The font cache. */ 51/* The font cache. */
41static Lisp_Object font_cache; 52static Lisp_Object font_cache;
42 53
54/* The scanline buffer. */
55static struct sfntfont_android_scanline_buffer scanline_buffer;
56
57/* The largest size of the scanline buffer since the last window
58 update. */
59static size_t max_scanline_buffer_size;
60
61
62
63/* Return a temporary buffer for storing scan lines.
64 Set BUFFER to the buffer upon success. */
65
66#define GET_SCANLINE_BUFFER(buffer, height, stride) \
67 do \
68 { \
69 size_t _size; \
70 \
71 if (INT_MULTIPLY_WRAPV (height, stride, &_size)) \
72 memory_full (SIZE_MAX); \
73 \
74 if (_size < MAX_ALLOCA) \
75 (buffer) = alloca (_size); \
76 else \
77 { \
78 if (_size > scanline_buffer.buffer_size) \
79 { \
80 (buffer) \
81 = scanline_buffer.buffer_data \
82 = xrealloc (scanline_buffer.buffer_data, \
83 _size); \
84 scanline_buffer.buffer_size = _size; \
85 } \
86 else if (_size <= scanline_buffer.buffer_size) \
87 (buffer) = scanline_buffer.buffer_data; \
88 /* This is unreachable but clang says it is. */ \
89 else \
90 emacs_abort (); \
91 \
92 max_scanline_buffer_size \
93 = max (_size, max_scanline_buffer_size); \
94 } \
95 } while (false);
96
43 97
44 98
45/* Scale each of the four packed bytes in P in the low 16 bits of P by 99/* Scale each of the four packed bytes in P in the low 16 bits of P by
@@ -205,8 +259,6 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
205 back_pixel &= ~0x00ff00ff; 259 back_pixel &= ~0x00ff00ff;
206 back_pixel |= rb >> 16 | rb << 16 | 0xff000000; 260 back_pixel |= rb >> 16 | rb << 16 | 0xff000000;
207 261
208 USE_SAFE_ALLOCA;
209
210 prepare_face_for_display (s->f, s->face); 262 prepare_face_for_display (s->f, s->face);
211 263
212 /* Build the scanline buffer. Figure out the bounds of the 264 /* Build the scanline buffer. Figure out the bounds of the
@@ -259,7 +311,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
259 /* Allocate enough to hold text_rectangle.height, aligned to 8 311 /* Allocate enough to hold text_rectangle.height, aligned to 8
260 bytes. Then fill it with the background. */ 312 bytes. Then fill it with the background. */
261 stride = (text_rectangle.width * sizeof *buffer) + 7 & ~7; 313 stride = (text_rectangle.width * sizeof *buffer) + 7 & ~7;
262 SAFE_NALLOCA (buffer, text_rectangle.height, stride); 314 GET_SCANLINE_BUFFER (buffer, text_rectangle.height, stride);
263 memset (buffer, 0, text_rectangle.height * stride); 315 memset (buffer, 0, text_rectangle.height * stride);
264 316
265 if (with_background) 317 if (with_background)
@@ -327,10 +379,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
327 /* If locking the bitmap fails, just discard the data that was 379 /* If locking the bitmap fails, just discard the data that was
328 allocated. */ 380 allocated. */
329 if (!bitmap_data) 381 if (!bitmap_data)
330 { 382 return;
331 SAFE_FREE ();
332 return;
333 }
334 383
335 /* Loop over each clip rect in the GC. */ 384 /* Loop over each clip rect in the GC. */
336 eassert (bitmap_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888); 385 eassert (bitmap_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);
@@ -366,8 +415,33 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from,
366 android_damage_window (FRAME_ANDROID_DRAWABLE (s->f), 415 android_damage_window (FRAME_ANDROID_DRAWABLE (s->f),
367 &text_rectangle); 416 &text_rectangle);
368 417
369 /* Release the temporary scanline buffer. */ 418#undef MAX_ALLOCA
370 SAFE_FREE (); 419}
420
421
422
423/* Shrink the scanline buffer after a window update. If
424 max_scanline_buffer_size is not zero, and is less than
425 scanline_buffer.buffer_size / 2, then resize the scanline buffer to
426 max_scanline_buffer_size. */
427
428void
429sfntfont_android_shrink_scanline_buffer (void)
430{
431 if (!max_scanline_buffer_size)
432 return;
433
434 if (max_scanline_buffer_size
435 < scanline_buffer.buffer_size / 2)
436 {
437 scanline_buffer.buffer_size
438 = max_scanline_buffer_size;
439 scanline_buffer.buffer_data
440 = xrealloc (scanline_buffer.buffer_data,
441 max_scanline_buffer_size);
442 }
443
444 max_scanline_buffer_size = 0;
371} 445}
372 446
373 447
@@ -437,10 +511,11 @@ loaded before character sets are made available. */)
437 511
438 while ((dirent = readdir (dir))) 512 while ((dirent = readdir (dir)))
439 { 513 {
440 /* If it contains (not ends with!) with .ttf, then enumerate 514 /* If it contains (not ends with!) with .ttf or .ttc, then
441 it. */ 515 enumerate it. */
442 516
443 if (strstr (dirent->d_name, ".ttf")) 517 if (strstr (dirent->d_name, ".ttf")
518 || strstr (dirent->d_name, ".ttc"))
444 { 519 {
445 sprintf (name, "%s/%s", system_font_directories[i], 520 sprintf (name, "%s/%s", system_font_directories[i],
446 dirent->d_name); 521 dirent->d_name);
diff --git a/src/sfntfont.c b/src/sfntfont.c
index 56977622211..e2d18517fcb 100644
--- a/src/sfntfont.c
+++ b/src/sfntfont.c
@@ -77,6 +77,9 @@ struct sfnt_font_desc
77 /* The header of the cmap being used. May be invalid, in which case 77 /* The header of the cmap being used. May be invalid, in which case
78 platform_id will be 500. */ 78 platform_id will be 500. */
79 struct sfnt_cmap_encoding_subtable subtable; 79 struct sfnt_cmap_encoding_subtable subtable;
80
81 /* The offset of the table directory within PATH. */
82 off_t offset;
80}; 83};
81 84
82/* List of fonts. */ 85/* List of fonts. */
@@ -426,15 +429,17 @@ sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc)
426 } 429 }
427} 430}
428 431
429/* Enumerate the font FILE into the list of system fonts. Return 1 if 432/* Enumerate the offset subtable SUBTABLES in the file FD, whose file
430 it could not be enumerated, 0 otherwise. */ 433 name is FILE. OFFSET should be the offset of the subtable within
434 the font file, and is recorded for future use. Value is 1 upon
435 failure, else 0. */
431 436
432int 437static int
433sfnt_enum_font (const char *file) 438sfnt_enum_font_1 (int fd, const char *file,
439 struct sfnt_offset_subtable *subtables,
440 off_t offset)
434{ 441{
435 struct sfnt_font_desc *desc; 442 struct sfnt_font_desc *desc;
436 int fd;
437 struct sfnt_offset_subtable *subtables;
438 struct sfnt_head_table *head; 443 struct sfnt_head_table *head;
439 struct sfnt_name_table *name; 444 struct sfnt_name_table *name;
440 struct sfnt_meta_table *meta; 445 struct sfnt_meta_table *meta;
@@ -444,18 +449,7 @@ sfnt_enum_font (const char *file)
444 desc = xzalloc (sizeof *desc + strlen (file) + 1); 449 desc = xzalloc (sizeof *desc + strlen (file) + 1);
445 desc->path = (char *) (desc + 1); 450 desc->path = (char *) (desc + 1);
446 memcpy (desc->path, file, strlen (file) + 1); 451 memcpy (desc->path, file, strlen (file) + 1);
447 452 desc->offset = offset;
448 /* Now open the font for reading. */
449 fd = emacs_open (file, O_RDONLY, 0);
450
451 if (fd == -1)
452 goto bail;
453
454 /* Read the table directory. */
455 subtables = sfnt_read_table_directory (fd);
456
457 if (!subtables)
458 goto bail0;
459 453
460 /* Check that this is a TrueType font. */ 454 /* Check that this is a TrueType font. */
461 if (subtables->scaler_type != SFNT_SCALER_TRUE 455 if (subtables->scaler_type != SFNT_SCALER_TRUE
@@ -511,8 +505,6 @@ sfnt_enum_font (const char *file)
511 xfree (meta); 505 xfree (meta);
512 xfree (name); 506 xfree (name);
513 xfree (head); 507 xfree (head);
514 xfree (subtables);
515 emacs_close (fd);
516 return 0; 508 return 0;
517 509
518 bail3: 510 bail3:
@@ -521,11 +513,84 @@ sfnt_enum_font (const char *file)
521 bail2: 513 bail2:
522 xfree (head); 514 xfree (head);
523 bail1: 515 bail1:
516 xfree (desc);
517 return 1;
518}
519
520/* Enumerate the font FILE into the list of system fonts. Return 1 if
521 it could not be enumerated, 0 otherwise.
522
523 FILE can either be a TrueType collection file containing TrueType
524 fonts, or a TrueType font itself. */
525
526int
527sfnt_enum_font (const char *file)
528{
529 int fd, rc;
530 struct sfnt_offset_subtable *subtables;
531 struct sfnt_ttc_header *ttc;
532 size_t i;
533
534 /* Now open the font for reading. */
535 fd = emacs_open (file, O_RDONLY, 0);
536
537 if (fd == -1)
538 goto bail;
539
540 /* Read the table directory. */
541 subtables = sfnt_read_table_directory (fd);
542
543 if (subtables == (struct sfnt_offset_subtable *) -1)
544 {
545 /* This is actually a TrueType container file. Go back to the
546 beginning and read the TTC header. */
547
548 if (lseek (fd, 0, SEEK_SET))
549 goto bail0;
550
551 ttc = sfnt_read_ttc_header (fd);
552
553 if (!ttc)
554 goto bail0;
555
556 /* Enumerate each of the fonts in the collection. */
557
558 for (i = 0; i < ttc->num_fonts; ++i)
559 {
560 if (lseek (fd, ttc->offset_table[i], SEEK_SET)
561 != ttc->offset_table[i])
562 continue;
563
564 subtables = sfnt_read_table_directory (fd);
565
566 if (!subtables)
567 continue;
568
569 sfnt_enum_font_1 (fd, file, subtables,
570 ttc->offset_table[i]);
571 xfree (subtables);
572 }
573
574 /* Always treat reading containers as having been
575 successful. */
576
577 emacs_close (fd);
578 xfree (ttc);
579 return 0;
580 }
581
582 if (!subtables)
583 goto bail0;
584
585 /* Now actually enumerate this font. */
586 rc = sfnt_enum_font_1 (fd, file, subtables, 0);
524 xfree (subtables); 587 xfree (subtables);
588 emacs_close (fd);
589 return rc;
590
525 bail0: 591 bail0:
526 emacs_close (fd); 592 emacs_close (fd);
527 bail: 593 bail:
528 xfree (desc);
529 return 1; 594 return 1;
530} 595}
531 596
@@ -1730,6 +1795,12 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
1730 if (fd == -1) 1795 if (fd == -1)
1731 goto bail; 1796 goto bail;
1732 1797
1798 /* Seek to the offset specified. */
1799
1800 if (desc->offset
1801 && lseek (fd, desc->offset, SEEK_SET) != desc->offset)
1802 goto bail;
1803
1733 /* Read the offset subtable. */ 1804 /* Read the offset subtable. */
1734 subtable = sfnt_read_table_directory (fd); 1805 subtable = sfnt_read_table_directory (fd);
1735 1806