home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / e20313sr.zip / emacs / 20.3.1 / src / pmemacs.c < prev    next >
C/C++ Source or Header  |  1999-07-31  |  151KB  |  4,892 lines

  1. /* pmemacs.c -- OS/2 Presentation Manager interface for GNU Emacs.
  2.    Copyright (C) 1993-1996 Eberhard Mattes.
  3.    Copyright (C) 1995 Patrick Nadeau (scroll bar code).
  4.  
  5. This file is part of GNU Emacs.
  6.  
  7. GNU Emacs is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2, or (at your option)
  10. any later version.
  11.  
  12. GNU Emacs is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. GNU General Public License for more details.
  16.  
  17. You should have received a copy of the GNU General Public License
  18. along with GNU Emacs; see the file COPYING.  If not, write to
  19. the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  20. Boston, MA 02111-1307, USA.  */
  21.  
  22.  
  23. #include <config.h>
  24. #include <stdio.h>
  25. #define _UCHAR_T
  26. #include "lisp.h"
  27. #include "termhooks.h"
  28.  
  29. #define INCL_DOS
  30. #define INCL_DOSERRORS
  31. #define INCL_WIN
  32. #define INCL_GPI
  33. #define INCL_DEV
  34. #include <os2.h>
  35. #include <stdlib.h>
  36. #include <stdarg.h>
  37. #include <stddef.h>
  38. #include <string.h>
  39. #include <unistd.h>
  40. #include <signal.h>
  41. #include "pmemacs.h"
  42.  
  43. #define HASH_SIZE 53
  44.  
  45. #define DIALOG_CODE_PAGE 850
  46.  
  47. #define UWM_CREATE              (WM_USER+1000) /* Create a window */
  48. #define UWM_DESTROY             (WM_USER+1001) /* Destroy a window */
  49. #define UWM_POPUPMENU           (WM_USER+1002) /* Create a popup menu */
  50. #define UWM_DIALOG              (WM_USER+1003) /* Create a dialog box */
  51. #define UWM_MENUBAR             (WM_USER+1004) /* Update the menubar */
  52. #define UWM_FILEDIALOG          (WM_USER+1005) /* Create a file dialog box */
  53. #define UWM_CREATE_SCROLLBAR    (WM_USER+1006) /* Create a scrollbar */
  54. #define UWM_DESTROY_SCROLLBAR   (WM_USER+1007) /* Destroy a scrollbar */
  55. #define UWM_INITIAL_SHOW        (WM_USER+1008) /* Call initial_show_frame() */
  56.  
  57. /* Cf. lisp/term/pm-win.el. */
  58. #define VK_CLOSE_FRAME          57 /* 0x39 */
  59. #define VK_KP_CENTER            58
  60. #define VK_KP_ADD               59
  61. #define VK_KP_SUBTRACT          60
  62. #define VK_KP_MULTIPLY          61
  63. #define VK_KP_DIVIDE            62
  64. #define VK_KP_0                 63
  65. #define VK_KP_1                 64
  66. #define VK_KP_2                 65
  67. #define VK_KP_3                 66
  68. #define VK_KP_4                 67
  69. #define VK_KP_5                 68
  70. #define VK_KP_6                 69
  71. #define VK_KP_7                 70
  72. #define VK_KP_8                 71
  73. #define VK_KP_9                 72
  74. #define VK_KP_SEPARATOR         73
  75. #define VK_KP_DECIMAL           74
  76.  
  77. #define BUTTON_DROP_FILE        5    /* See keyboard.c */
  78. #define BUTTON_DROP_COLOR       6
  79.  
  80. #define KC_ALTGR                0x10000
  81.  
  82. #define IDM_MENU                    1
  83. #define IDM_MENU_LAST            9999
  84. #define IDM_MENUBAR             10000
  85. #define IDM_MENUBAR_LAST        19999
  86. #define IDM_MENUBAR_TOP         20000
  87. #define IDM_SUBMENU             21000
  88.  
  89. /* Maximum number of scrollbars */
  90. #define MAX_SCROLLBARS          128
  91.  
  92. /* Window ID of first scrollbar.  IDs FID_SCROLLBAR through
  93.    FID_SCROLLBAR + MAX_SCROLLBARS - 1 are reserved for scrollbars.
  94.    Most of our windows just use an ID of zero.  */
  95. #define FID_SCROLLBAR           256
  96.  
  97. /* The font_spec structure contains a font specification derived from
  98.    a font name string. */
  99. typedef struct
  100. {
  101.   /* The size of the font, in printer's points multipled by 10. */
  102.   int size;
  103.  
  104.   /* Font selection: FATTR_SEL_UNDERSCORE etc. */
  105.   int sel;
  106.  
  107.   /* The face name of the font. */
  108.   char name[FACESIZE];
  109. } font_spec;
  110.  
  111. /* The face_data structure defines a face (a font with attributes). */
  112. typedef struct
  113. {
  114.   COLOR foreground;
  115.   COLOR background;
  116.   int font_id;                  /* Zero if no font associated */
  117.   font_spec spec;
  118. } face_data;
  119.  
  120. /* The font_data structure defines a font. */
  121. typedef struct
  122. {
  123.   /* Face name, size and selection of the font. */
  124.   font_spec spec;
  125.  
  126.   /* Size of character box, for outline fonts.  This value is used for
  127.      both cx and cy.  This value is undefined for bitmap fonts. */
  128.   FIXED cbox_size;
  129.  
  130.   /* This flag is non-zero if an outline font is selected. */
  131.   char outline;
  132.  
  133.   /* This flag is non-zero if the width of the font is wrong or if the
  134.      font is an outline font -- we have to use GpiCharStringPosAt
  135.      instead of GpiCharStringAt in that case. */
  136.   char use_incr;
  137. } font_data;
  138.  
  139.  
  140. /* The frame_data structure holds all the data for a frame.  A pointer
  141.    to this structure is stored at position 0 of the client window data
  142.    area. */
  143. typedef struct frame_data
  144. {
  145.  
  146.     /* This member is used for chaining frames that share the same hash
  147.        code. */
  148.     struct frame_data *next;
  149.  
  150.     /* The width and height of the client window. */
  151.     LONG cxClient, cyClient;
  152.  
  153.     /* Some font metrics: the character width, character height and
  154.        descender height of the font.  All values are given in pixels. */
  155.     LONG cxChar, cyChar, cyDesc;
  156.  
  157.     /* The frame window handle. */
  158.     HWND hwndFrame;
  159.  
  160.     /* The client window handle.  The client window is a child window of
  161.        the frame window HWNDFRAME. */
  162.     HWND hwndClient;
  163.  
  164.     /* This presentation space handle is used for asynchronous painting
  165.        of the client window. */
  166.     HPS hpsClient;
  167.  
  168.     /* The background color of the default face of this frame, */
  169.     COLOR background_color;
  170.  
  171.     /* The position of the cursor, in characters. */
  172.     int cursor_x, cursor_y;
  173.  
  174.     /* This member is non-zero if the cursor should be visible. */
  175.     int cursor_on;
  176.  
  177.     /* The type of the cursor, one of the CURSORTYPE_whatever constants. */
  178.     int cursor_type;
  179.  
  180.     /* The width of the cursor, for CURSORTYPE_BAR. */
  181.  
  182.     int cursor_width;
  183.  
  184.     /* Whether the cursor is blinking or not. */
  185.     int cursor_blink;
  186.  
  187.     /* The width and height, respectively, in characters of this frame. */
  188.     int width, height;
  189.  
  190.     /* The width of the vertical scrollbars, in characters. */
  191.     int sb_width;
  192.  
  193.     /* The lower 3 bits of this member are set if WM_BUTTONxUP should be
  194.        ignored until WM_BUTTONxDOWN is received.  This member is set to 7
  195.        (all three bits set) when a button message is received while the
  196.        window doesn't have the focus.  A bit is cleared if a
  197.        WM_BUTTONxDOWN message is received.  Bit 0 is used for button 1,
  198.        bit 1 is used for button 2, and bit 2 is used for button 3. */
  199.     int ignore_button_up;
  200.  
  201.     /* The identity of the frame.  In Emacs, this is the frame pointer.
  202.        As we do hashing on this value, it is cast to unsigned long in
  203.        pmterm.c before passing to this module. */
  204.     ULONG id;
  205.  
  206.     /* If there is a popup menu, this member holds the handle for that
  207.        menu.  Otherwise, it is NULL. */
  208.     HWND hwndPopupMenu;
  209.  
  210.     /* If there is a menubar, this member holds the handle for that
  211.        menu.  Otherwise, it is NULL. */
  212.     HWND hwndMenubar;
  213.  
  214.     /* This array is used for mapping menu IDs of the menubar to
  215.        selection indices used by Emacs. */
  216.     int *menubar_id_map;
  217.  
  218.     /* The number of elements allocated for `menubar_id_map'. */
  219.     int menubar_id_allocated;
  220.  
  221.     /* The number of elements currently used in `menubar_id_map'. */
  222.     int menubar_id_used;
  223.  
  224.     /* The following members hold the pm_menu structures and strings
  225.        which were used most recently for building the menubar.  This
  226.        data is used to decide whether the menubar has changed. */
  227.     int cur_menubar_el_allocated;
  228.     pm_menu *cur_menubar_el;
  229.     int cur_menubar_str_allocated;
  230.     char *cur_menubar_str;
  231.  
  232.     /* The following fields are used for recording the menubar contents
  233.        while a frame has not yet been completely created. */
  234.     int initial_menubar_el_count;
  235.     int initial_menubar_str_bytes;
  236.  
  237.     /* This flag is set if a popup menu is active. */
  238.     int popup_active;
  239.  
  240.     /* This is the default font of this frame. */
  241.     font_spec font;
  242.  
  243.     /* This is the font (size.name, for instance "8.Helv") for menus of
  244.        this frame.  See also pmemacs.h. */
  245.     char menu_font[64];
  246.  
  247.     /* This field records the stage of frame creation. */
  248.     enum
  249.     {
  250.         STAGE_CREATING,             /* pm-create-frame in progress */
  251.         STAGE_INVISIBLE,            /* pm-create-frame completed, invisible */
  252.         STAGE_GETTING_READY,        /* Transition from INVISIBLE to READY */
  253.         STAGE_READY                 /* Frame creation completed */
  254.     } stage;
  255.  
  256.     /* This flag is valid only if the STAGE field above is less than
  257.        STAGE_READY.  It indicates whether the frame is visible (1),
  258.        invisible (0), or iconified (-1). */
  259.     int initial_visibility;
  260.  
  261.     /* While creating the frame, the position is kept here.  These
  262.        fields are valid while STAGE is less than STAGE_READY. */
  263.     int initial_left, initial_left_base;
  264.     int initial_top, initial_top_base;
  265.  
  266.     /* This flag is non-zero when the window is in the process of being
  267.        minimized.  It is set when a WM_MINMAXFRAME sent to the frame
  268.        window indicates that the window is being minimized.  The flag is
  269.        used when processing the WM_SIZE message: if it is set, WM_SIZE
  270.        is ignored and the flag is cleared. */
  271.     int minimizing;
  272.  
  273.     /* This flag is non-zero when the window is in the process of being
  274.        restored from minimized state.  It is set when a WM_MINMAXFRAME
  275.        sent to the frame window indicates that the window is being
  276.        restored.  The flag is used when processing the WM_SIZE message:
  277.        if it is set, WM_SIZE clears this flag and calls set_size() in
  278.        case the size has been changed while the window was minimized. */
  279.     int restoring;
  280.  
  281.     /* This flag is non-zero if the window is minimized. */
  282.     int minimized;
  283.  
  284.     /* Ignore WM_SIZE if this flag is non-zero. */
  285.     int ignore_wm_size;
  286.  
  287.     /* The Emacs modifier bit to be sent if the left Alt key is
  288.        pressed. */
  289.     int alt_modifier;
  290.  
  291.     /* The Emacs modifier bit to be sent if the right Alt key (AltGr on
  292.        most non-US keyboards) is pressed. */
  293.     int altgr_modifier;
  294.  
  295.     /* The most recently pressed deadkey.  When receiving an invalid
  296.        composition message, both the deadkey and the next key are sent
  297.        to Emacs.  If this member is zero, no deadkey has been pressed
  298.        recently. */
  299.     int deadkey;
  300.  
  301.     /* Disable PM-defined shortcuts (accelerators) such as F1, F10,
  302.        Alt+F4, for which the associated bit in this member is zero.  */
  303.     unsigned shortcuts;
  304.  
  305.     /* This array maps OS/2 mouse button numbers to Emacs mouse button
  306.        numbers.  The values are either 0 (if the button is to be
  307.        ignored) or are in 1 through 3. */
  308.     char buttons[3];
  309.  
  310.     /* This array contains non-zero values for all logical fonts defined
  311.        for this frame. */
  312.     char font_defined[255];
  313.  
  314.     /* List of character increments, based on the default font.  This is
  315.        used to override non-integer character increments when using an
  316.        outline font and to override wrong character increments when
  317.        using a font of a size differing from the size of the default
  318.        font. */
  319.     LONG increments[512];
  320.  
  321.     /* Accept variable-width fonts if `var_width_fonts' is non-zero. */
  322.     char var_width_fonts;
  323.  
  324. #ifdef USE_PALETTE_MANAGER
  325.     /* The palette. */
  326.     HPAL hpal;
  327. #endif
  328.  
  329.     /* Current state of various PM attributes.  This is used to speed up
  330.        text output by avoiding GPI calls. */
  331.     COLOR cur_color, cur_backcolor;
  332.     LONG cur_charset, cur_backmix;
  333.     FIXED cur_cbox_size;
  334. } frame_data;
  335.  
  336.  
  337. typedef struct
  338. {
  339.   int x, y, button, align_top;
  340.   pm_menu *data;
  341.   char *str;
  342. } menu_data;
  343.  
  344. typedef struct
  345. {
  346.   pm_menu *data;
  347.   char *str;
  348.   int el_count;
  349.   int str_bytes;
  350. } menubar_data;
  351.  
  352. struct drop
  353. {
  354.   unsigned long cookie;
  355.   unsigned len;
  356.   char str[CCHMAXPATH];
  357. };
  358.  
  359. struct scrollbar_entry
  360. {
  361.   HWND hwnd;
  362.   slider async;                 /* Used while dragging */
  363.   short range;
  364. };
  365.  
  366. /* The original window procedure of frame windows.  Frame windows are
  367.    subclassed by FrameWndProc, which calls this window procedure. */
  368. static PFNWP old_frame_proc = NULL;
  369.  
  370. /* The anchor-block handle.  It is shared by all threads.  This should
  371.    be fixed, each thread that has a message queue should have its own
  372.    anchor-block handle.  Currently, OS/2 ignores the anchor-block
  373.    handle. */
  374. static HAB hab;
  375.  
  376. /* The message queue handle of the PM thread. */
  377. static HMQ hmq;
  378.  
  379. /* The size of the screen. */
  380. static SWP swpScreen;
  381.  
  382. /* Requests from Emacs arrive in this pipe. */
  383. static int inbound_pipe;
  384.  
  385. /* Events are sent through this pipe to Emacs. */
  386. static int outbound_pipe;
  387.  
  388. /* The handle of the object window of the PM thread.  See
  389.    ObjectWndProc() for details. */
  390. static HWND hwndObject;
  391.  
  392. /* This hash table is used for finding the frame_data structure for a
  393.    frame ID. */
  394. static frame_data *hash_table[HASH_SIZE];
  395.  
  396. /* Pointer to frame_data structure used while creating a new frame.
  397.    This is dirty. */
  398. static frame_data *new_frame;
  399.  
  400. /* Window class names. */
  401. static const char szFrameClass[] = "pmemacs.frame";
  402. static const char szClientClass[] = "pmemacs.client";
  403. static const char szObjectClass[] = "pmemacs.object";
  404.  
  405. /* This flag is non-zero if a WM_MENUSELECT message has been
  406.    received. */
  407. static int menu_selected = FALSE;
  408.  
  409. /* This variable is used for setting the IDs of submenus.  */
  410. static int submenu_id;
  411.  
  412. /* Map scrollbar IDs to window handles.  Unused entries contain
  413.    NULLHANDLE in the `hwnd' member.  This table is shared by all
  414.    frames.  Per-scrollbar data is also stored in this table. */
  415.  
  416. static struct scrollbar_entry scrollbar_table[MAX_SCROLLBARS];
  417.  
  418. /* While dragging the slider of a scrollbar, we should not set the
  419.    position or size of the slider (that would confuse the PM).  As the
  420.    scrollbar code captures the mouse, only one scrollbar can be
  421.    dragged at any time, therefore a global variable is sufficient for
  422.    recording whether the slider of the current scrollbar is being
  423.    dragged or not.  As that scrollbar also has the keyboard focus,
  424.    keyboard commands cannot be used to cause changes to the
  425.    scrollbars.  For now, we ignore the problem of Lisp code causing
  426.    changes to the scrollbar during dragging. */
  427. static int dragging_scrollbar_slider = FALSE;
  428.  
  429. /* For positioning popup menus so that the first item is at the
  430.    position of the mouse, we need to remember the first ID.  */
  431. static int first_id;
  432.  
  433. /* This is a presentation space for the screen.  It is used by
  434.    dialog_string_width() which assumes that the default font is
  435.    selected. */
  436. static HPS hpsScreen;
  437.  
  438. /* This is the width of an icon, in pixels. */
  439. static LONG cxIcon;
  440.  
  441. /* This is the maximum width and height, respectively, of the system
  442.    font.  These values are used for converting to and from dialog box
  443.    units. */
  444. static LONG default_font_width;
  445. static LONG default_font_height;
  446.  
  447. /* Post this event semaphore to beep. */
  448. static HEV hevBeep;
  449.  
  450. /* Post this event after processing a message sent by pipe_thread(). */
  451. static HEV hevDone;
  452.  
  453. /* The process ID of Emacs proper.  This is taken from the command
  454.    line.  We cannot use getppid(), as PMSHELL is the parent process of
  455.    all PM sessions. */
  456. static int emacs_pid;
  457.  
  458. /* The quit character.  If this character is typed, send SIGINT to
  459.    Emacs instead of a keyboard event.  The default is C-G. */
  460. static int quit_char = 0x07;
  461.  
  462. /* This variable is TRUE after receiving a PMR_CLOSE request.  This is
  463.    for avoiding errors due to broken pipes etc. */
  464. static int terminating = FALSE;
  465.  
  466. /* The table of faces.  Element 0 of face_vector is not used. */
  467. static face_data *face_vector = NULL;
  468. static int nfaces = 0;
  469. static int nfaces_allocated = 0;
  470.  
  471. /* The table of fonts.  Local font IDs are shared by all frames to
  472.    simplify things.  The Presentation Manager supports local font IDs
  473.    1 through 254 and 0 (the default font). */
  474. static font_data font_vector[255];
  475. static int nfonts = 0;
  476.  
  477. /* If this variable is true, send PME_MOUSEMOVE events for
  478.    WM_MOUSEMOVE messages.  As Emacs 19.23 uses mouse movement events
  479.    for highlighting mouse-sensitive areas, track_mouse is now always
  480.    true.  Perhaps later versions of Emacs want to turn off mouse
  481.    movement events, therefore we keep this variable and the
  482.    PMR_TRACKMOUSE request. */
  483.  
  484. static int track_mouse = TRUE;
  485.  
  486. /* The glyph coordinates of the previous PME_MOUSEMOVE event. */
  487. static int track_x = -1;
  488. static int track_y = -1;
  489.  
  490. /* This flag indicates whether the mouse is captured or not. */
  491. static int capture_flag = FALSE;
  492.  
  493. #ifdef USE_PALETTE_MANAGER
  494.  
  495. /* This variable is TRUE if the palette manager is available. */
  496. static int has_palette_manager = FALSE;
  497.  
  498. /* The color table for the palette manager */
  499. static ULONG color_table[1024];
  500. static int colors = 0;
  501.  
  502. #endif
  503.  
  504. #ifdef USE_PALETTE_MANAGER
  505. #define GET_COLOR(hps,x) (has_palette_manager ? \
  506.                           GpiQueryColorIndex (hps, 0, (x)) : (x))
  507. #else
  508. #define GET_COLOR(hps,x) (x)
  509. #endif
  510.  
  511. /* This vector tells DlgProc which buttons are to be disabled. */
  512.  
  513. static char disabled_buttons[80];
  514.  
  515. /* The serial number of the last PMR_POPUPMENU or PMR_MENU request. */
  516.  
  517. static int menu_serial;
  518.  
  519. /* The serial number of the last PMR_DIALOG request. */
  520.  
  521. static int dialog_serial;
  522.  
  523. /* Circular buffer of objects recently dropped.  */
  524.  
  525. #define DROP_MAX 8
  526. static int drop_in = 0;
  527. static int drop_out = 0;
  528. static int drop_count = 0;
  529. static HMTX drop_mutex;
  530. static struct drop *drop_list;
  531.  
  532. /* Each drop action is assigned a number, for relating drop events
  533.    with PMR_DROP requests. */
  534.  
  535. static int drop_cookie = 0;
  536.  
  537. /* Current code page. */
  538.  
  539. static int code_page = 850;
  540.  
  541.  
  542. /* This table maps keypad keys. */
  543.  
  544. static const struct
  545. {
  546.   unsigned ch;                  /* Character code (input) */
  547.   unsigned sc;                  /* Scan code (input) */
  548.   unsigned vk;                  /* Virtual key (output) */
  549.   unsigned clr;                 /* Flags to clear (output) */
  550. } keypad[] =
  551.   {
  552.     {'*',  0x37, VK_KP_MULTIPLY,  0},
  553.     {'7',  0x47, VK_KP_7,         KC_SHIFT},
  554.     {'8',  0x48, VK_KP_8,         KC_SHIFT},
  555.     {'9',  0x49, VK_KP_9,         KC_SHIFT},
  556.     {'-',  0x4a, VK_KP_SUBTRACT,  0},
  557.     {'4',  0x4b, VK_KP_4,         KC_SHIFT},
  558.     {'5',  0x4c, VK_KP_5,         KC_SHIFT},
  559.     {'6',  0x4d, VK_KP_6,         KC_SHIFT},
  560.     {'+',  0x4e, VK_KP_ADD,       0},
  561.     {'1',  0x4f, VK_KP_1,         KC_SHIFT},
  562.     {'2',  0x50, VK_KP_2,         KC_SHIFT},
  563.     {'3',  0x51, VK_KP_3,         KC_SHIFT},
  564.     {'0',  0x52, VK_KP_0,         KC_SHIFT},
  565.     {'.',  0x53, VK_KP_DECIMAL,   KC_SHIFT},
  566.     {',',  0x53, VK_KP_SEPARATOR, KC_SHIFT},
  567.     {'/',  0x5c, VK_KP_DIVIDE,    0},
  568.   };
  569.  
  570.  
  571. /* Prototypes for functions which are used before defined. */
  572.  
  573. static void terminate_process (void);
  574.  
  575.  
  576. /* Convert between dialog box units and pixels. */
  577.  
  578. #define DLGUNIT_TO_PIX_X(x) (((x) * default_font_width) / 4)
  579. #define DLGUNIT_TO_PIX_Y(y) (((y) * default_font_height) / 8)
  580. #define PIX_TO_DLGUNIT_X(x) (((x) * 4 + default_font_width - 1) \
  581.                              / default_font_width)
  582. #define PIX_TO_DLGUNIT_Y(y) (((y) * 8 + default_font_height - 1) \
  583.                              / default_font_height)
  584.  
  585. /* Convert character coordinates to pixel coordinates (reference
  586.    point!). */
  587.  
  588. #define make_x(f,x) ((x) * (f)->cxChar)
  589. #define make_y(f,y) ((f)->cyClient + (f)->cyDesc - ((y) + 1) * (f)->cyChar)
  590.  
  591. /* Convert pixel coordinates to character coordinates (character
  592.    box!). */
  593.  
  594. #define charbox_x(f,x) ((x) / (f)->cxChar)
  595. #define charbox_y(f,y) (((f)->cyClient - 1 - (y)) / (f)->cyChar)
  596.  
  597.  
  598.  
  599. #ifdef TRACE
  600.  
  601. #define TRACE_FNAME             "pmemacs.log"
  602. #define TRACE_OFF               ((HFILE)-1)
  603.  
  604. static HFILE trace_handle = TRACE_OFF;
  605.  
  606. static void trace_str (const char *s, size_t len)
  607. {
  608.   ULONG n;
  609.  
  610.   if (trace_handle != TRACE_OFF &&
  611.       DosWrite (trace_handle, s, len, &n) != 0)
  612.     trace_handle = TRACE_OFF;
  613. }
  614.  
  615. #define TRACE_STR(S,LEN) trace_str ((S), (LEN))
  616. #define TRACE_PSZ(S)     trace_str ((S), strlen (S))
  617.  
  618. #else
  619.  
  620. #define TRACE_STR(S,LEN) ((void)0)
  621. #define TRACE_PSZ(S)     ((void)0)
  622.  
  623. #endif
  624.  
  625.  
  626. /* An error occured.  Display MSG in a message box, then quit. */
  627.  
  628. static void pm_error (const char *msg)
  629. {
  630.   WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, msg, "PM Emacs error", 0,
  631.                  MB_MOVEABLE | MB_OK | MB_ICONEXCLAMATION);
  632.   /*...*/
  633.   DosExit (EXIT_PROCESS, 1);
  634. }
  635.  
  636.  
  637. /* Display a message.  This is used for debugging. */
  638.  
  639. static void pm_message (HWND hwndOwner, const char *fmt, ...)
  640. {
  641.   va_list arg_ptr;
  642.   char tmp[200];
  643.  
  644.   va_start (arg_ptr, fmt);
  645.   vsprintf (tmp, fmt, arg_ptr);
  646.   WinMessageBox (HWND_DESKTOP, hwndOwner, tmp, "PM Emacs", 0,
  647.                  MB_MOVEABLE | MB_OK | MB_ICONEXCLAMATION);
  648. }
  649.  
  650. /* Allocate memory.  Note that DosAllocMem fails if SIZE is 0. */
  651.  
  652. static void *allocate (ULONG size)
  653. {
  654.   ULONG rc;
  655.   void *p;
  656.  
  657.   if (size == 0)
  658.     return (NULL);
  659.   rc = DosAllocMem ((PVOID)&p, size, PAG_COMMIT | PAG_READ | PAG_WRITE);
  660.   if (rc != 0)
  661.     pm_error ("Out of memory.");
  662.   return (p);
  663. }
  664.  
  665.  
  666. /* Deallocate memory allocated by allocate(). */
  667.  
  668. static void deallocate (void *p)
  669. {
  670.   if (p != NULL)
  671.     DosFreeMem (p);
  672. }
  673.  
  674.  
  675. /* Send data to Emacs. */
  676.  
  677. static void send (int fd, const void *src, size_t size)
  678. {
  679.   const char *s;
  680.   ULONG rc, n;
  681.  
  682.   if (terminating)
  683.     return;
  684.   s = src;
  685.   while (size != 0)
  686.     {
  687.       rc = DosWrite (fd, s, size, &n);
  688.       if (rc != 0 || n == 0)
  689.         pm_message (HWND_DESKTOP, "Cannot write to pipe, rc=%lu, n=%lu",
  690.                     rc, n);
  691.       size -= n;
  692.       s += n;
  693.     }
  694. }
  695.  
  696.  
  697. /* Send an event to Emacs. */
  698.  
  699. static void send_event (const pm_event *src)
  700. {
  701.   send (outbound_pipe, src, sizeof (pm_event));
  702. }
  703.  
  704. /* Send answer to Emacs.  SERIAL is the serial number, taken from the
  705.    request.  SRC points to SIZE bytes of data.  If SRC is NULL, the
  706.    word ONE_WORD is sent instead. */
  707.  
  708. static void send_answer (int serial, const void *src, unsigned size,
  709.                          int one_word)
  710. {
  711.   pm_event pme;
  712.  
  713.   pme.answer.header.type = PME_ANSWER;
  714.   pme.answer.header.frame = 0;
  715.   pme.answer.serial = serial;
  716.   pme.answer.one_word = one_word;
  717.   pme.answer.size = size;
  718.   if (src == NULL)
  719.     {
  720.       pme.answer.size = -1;
  721.       send_event (&pme);
  722.     }
  723.   else if (size == 0)
  724.     send_event (&pme);
  725.   else
  726.     {
  727.       char *tem, *alloc;
  728.       unsigned total;
  729.  
  730.       /* Send the answer atomically to avoid interference with events
  731.          sent by other threads. */
  732.  
  733.       alloc = NULL;
  734.       total = size + sizeof (pme);
  735.       if (total <= 0x2000)
  736.         tem = alloca (total);
  737.       else
  738.         tem = alloc = allocate (total);
  739.       memcpy (tem, &pme, sizeof (pme));
  740.       memcpy (tem + sizeof (pme), src, size);
  741.       send (outbound_pipe, tem, total);
  742.       if (alloc != NULL) deallocate (alloc);
  743.     }
  744. }
  745.  
  746.  
  747. /* Clear all fonts of all the faces and reset the font IDs in the face
  748.    table. */
  749.  
  750. static void clear_fonts (void)
  751. {
  752.   frame_data *f;
  753.   int i;
  754.  
  755.   for (i = 1; i <= nfaces; ++i)
  756.     face_vector[i].font_id = 0;
  757.   for (i = 0; i < HASH_SIZE; ++i)
  758.     for (f = hash_table[i]; f != NULL; f = f->next)
  759.       memset (f->font_defined, 0, sizeof (f->font_defined));
  760.   nfonts = 0;
  761. }
  762.  
  763.  
  764. /* Compare two font specifications. */
  765.  
  766. static int equal_font_spec (const font_spec *f1, const font_spec *f2)
  767. {
  768.   return (f1->size == f2->size
  769.           && f1->sel == f2->sel
  770.           && strcmp (f1->name, f2->name) == 0);
  771. }
  772.  
  773.  
  774. /* Parse a font string into a font specification. */
  775.  
  776. #define SKIP_FIELD while (*str != 0 && *str != '-') ++str; \
  777.            if (*str == '-') ++str; else return (FALSE)
  778.  
  779. static int parse_font_spec (font_spec *dst, const unsigned char *str)
  780. {
  781.   int i;
  782.  
  783.   dst->size = 0;
  784.   dst->sel = 0;
  785.   dst->name[0] = 0;
  786.   if (str[0] == '-')
  787.     {
  788.       ++str;
  789.       SKIP_FIELD;               /* Foundry */
  790.       i = 0;
  791.       while (*str != 0 && *str != '-')
  792.         {
  793.           if (i < sizeof (dst->name) - 1)
  794.             dst->name[i++] = *str;
  795.           ++str;
  796.         }
  797.       dst->name[i] = 0;
  798.       SKIP_FIELD;               /* Family */
  799.       if (*str == 'b')
  800.         dst->sel |= FATTR_SEL_BOLD;
  801.       SKIP_FIELD;               /* Weight */
  802.       if (*str == 'i')
  803.         dst->sel |= FATTR_SEL_ITALIC;
  804.       SKIP_FIELD;               /* Slant */
  805.       SKIP_FIELD;               /* Width */
  806.       SKIP_FIELD;               /* Additional style */
  807.       SKIP_FIELD;               /* Pixel size */
  808.       i = 0;
  809.       while (*str >= '0' && *str <= '9')
  810.         {
  811.           i = i * 10 + (*str - '0');
  812.           ++str;
  813.         }
  814.       if (i == 0)
  815.         return (FALSE);
  816.       dst->size = i;
  817.       SKIP_FIELD;               /* Point size */
  818.       SKIP_FIELD;               /* X resolution */
  819.       SKIP_FIELD;               /* Y resolution */
  820.       SKIP_FIELD;               /* Spacing */
  821.       SKIP_FIELD;               /* Registry */
  822.       return (TRUE);
  823.     }
  824.   else
  825.     {
  826.       while (*str >= '0' && *str <= '9')
  827.         {
  828.           dst->size = dst->size * 10 + (*str - '0');
  829.           ++str;
  830.         }
  831.       dst->size *= 10;
  832.       if (dst->size == 0)
  833.         return (FALSE);
  834.       for (;;)
  835.         {
  836.           if (strncmp (str, ".bold", 5) == 0)
  837.             {
  838.               dst->sel |= FATTR_SEL_BOLD;
  839.               str += 5;
  840.             }
  841.           else if (strncmp (str, ".italic", 7) == 0)
  842.             {
  843.               dst->sel |= FATTR_SEL_ITALIC;
  844.               str += 7;
  845.             }
  846.           else if (*str == '.')
  847.             {
  848.               ++str;
  849.               break;
  850.             }
  851.           else
  852.             return (FALSE);
  853.         }
  854.       if (*str == 0)
  855.         return (FALSE);
  856.       _strncpy (dst->name, str, sizeof (dst->name));
  857.     }
  858.   return (TRUE);
  859. }
  860.  
  861.  
  862. /* Create a logical font (using ID) matching PFM.  SELECTION contains
  863.    font selection bits such as FATTR_SEL_UNDERSCORE. */
  864.  
  865. static int use_font (frame_data *f, int id, PFONTMETRICS pfm, int selection)
  866. {
  867.   FATTRS font_attrs;
  868.  
  869.   font_attrs.usRecordLength = sizeof (font_attrs);
  870.   font_attrs.fsSelection = selection;
  871.   font_attrs.lMatch = pfm->lMatch;
  872.   strcpy (font_attrs.szFacename, pfm->szFacename);
  873.   font_attrs.idRegistry = 0;
  874.   font_attrs.usCodePage = code_page;
  875.   font_attrs.lMaxBaselineExt = 0;
  876.   font_attrs.lAveCharWidth = 0;
  877.   font_attrs.fsType = 0;
  878.   font_attrs.fsFontUse = 0;
  879.   if (GpiCreateLogFont (f->hpsClient, NULL, id, &font_attrs) == GPI_ERROR)
  880.     return (FALSE);
  881.   GpiSetCharSet (f->hpsClient, id);
  882.   f->cur_charset = id;
  883.   return (TRUE);
  884. }
  885.  
  886.  
  887. /* Select a font matching FONT_NAME and FONT_SIZE (10 * pt).  If there
  888.    is such a font, return TRUE.  Otherwise, no font is selected and
  889.    FALSE is returned. */
  890.  
  891. static int select_font_internal (frame_data *f, int id, char *font_name,
  892.                                  int font_size, int selection,
  893.                                  font_data *font)
  894. {
  895.   ULONG fonts, temp, i;
  896.   LONG xRes;
  897.   USHORT size;
  898.   PFONTMETRICS pfm;
  899.   HDC hdc;
  900.  
  901.   temp = 0;
  902.   fonts = GpiQueryFonts (f->hpsClient, QF_PUBLIC | QF_PRIVATE, font_name,
  903.                          &temp, sizeof (FONTMETRICS), NULL);
  904.   pfm = allocate (fonts * sizeof (FONTMETRICS));
  905.   GpiQueryFonts (f->hpsClient, QF_PUBLIC | QF_PRIVATE, font_name,
  906.                  &fonts, sizeof (FONTMETRICS), pfm);
  907.   for (i = 0; i < fonts; ++i)
  908.     if ((f->var_width_fonts || (pfm[i].fsType & FM_TYPE_FIXED))
  909.         && !(pfm[i].fsDefn & FM_DEFN_OUTLINE)
  910.         && pfm[i].sNominalPointSize == font_size
  911.         && use_font (f, id, pfm+i, selection))
  912.       {
  913.         font->outline = FALSE;
  914.         font->use_incr = (selection != f->font.sel
  915.                           || !(pfm[i].fsType & FM_TYPE_FIXED)
  916.                           || pfm[i].lMaxCharInc != f->cxChar
  917.                           || pfm[i].lMaxBaselineExt != f->cyChar
  918.                           || pfm[i].lMaxDescender != f->cyDesc);
  919.         deallocate (pfm);
  920.         return (TRUE);
  921.       }
  922.   for (i = 0; i < fonts; ++i)
  923.     if ((f->var_width_fonts || (pfm[i].fsType & FM_TYPE_FIXED))
  924.         && (pfm[i].fsDefn & FM_DEFN_OUTLINE)
  925.         && use_font (f, id, pfm+i, selection))
  926.       {
  927.         font->outline = TRUE;
  928.         font->use_incr = TRUE;
  929.         hdc = GpiQueryDevice (f->hpsClient);
  930.         DevQueryCaps (hdc, CAPS_HORIZONTAL_FONT_RES, 1, &xRes);
  931.         size = (USHORT)((double)font_size * (double)xRes / 720.0 + 0.5);
  932.         font->cbox_size = MAKEFIXED (size, 0);
  933.         deallocate (pfm);
  934.         return (TRUE);
  935.       }
  936.   deallocate (pfm);
  937.   return (FALSE);
  938. }
  939.  
  940.  
  941. /* Select a font matching FONT_NAME and FONT_SIZE (10 * pt).  If this
  942.    fails, use the default font of the frame.  If there is such a font,
  943.    return TRUE.  Otherwise, no font is selected and FALSE is
  944.    returned. */
  945.  
  946. static int select_font (frame_data *f, int id, char *font_name,
  947.                         int font_size, int selection, font_data *font)
  948. {
  949.   int ok;
  950.  
  951.   ok = select_font_internal (f, id, font_name, font_size, selection, font);
  952.   if (!ok)
  953.     ok = select_font_internal (f, id, f->font.name, f->font.size, selection,
  954.                                font);
  955.   return (ok);
  956. }
  957.  
  958.  
  959. /* Create a font for a face. */
  960.  
  961. static int make_font (frame_data *f, face_data *face)
  962. {
  963.   int i;
  964.   font_data *font;
  965.   font_spec spec;
  966.  
  967.   spec = face->spec;
  968.   if (spec.size == 0)
  969.     spec.size = f->font.size;
  970.   if (spec.name[0] == 0)
  971.     strcpy (spec.name, f->font.name);
  972.   if (face->font_id == 0)
  973.     for (i = 1; i <= nfonts; ++i)
  974.       if (equal_font_spec (&font_vector[i].spec, &spec))
  975.         {
  976.           face->font_id = i;
  977.           break;
  978.         }
  979.   if (face->font_id == 0)
  980.     {
  981.       if (nfonts >= 254)
  982.         face->font_id = 1;
  983.       else
  984.         {
  985.           /* Note: Font ID 0 is not used, it means no font ID */
  986.           face->font_id = ++nfonts;
  987.           font_vector[face->font_id].spec = spec;
  988.         }
  989.     }
  990.   font = &font_vector[face->font_id];
  991.   select_font (f, face->font_id, font->spec.name, font->spec.size,
  992.                spec.sel, font);
  993.   f->font_defined[face->font_id] = 1;
  994.   return (face->font_id);
  995. }
  996.  
  997.  
  998. /* Set the default font of the frame F, according to the font_name and
  999.    font_size fields of *F. */
  1000.  
  1001. static void set_default_font (frame_data *f)
  1002. {
  1003.   FONTMETRICS fm;
  1004.   SIZEF cbox;
  1005.   int i;
  1006.   font_data font;
  1007.  
  1008.   clear_fonts ();
  1009.   if (!select_font_internal (f, 1, f->font.name, f->font.size, 0, &font))
  1010.     {
  1011.       parse_font_spec (&f->font, DEFAULT_FONT);
  1012.       select_font_internal (f, 1, f->font.name, f->font.size, 0, &font);
  1013.     }
  1014.   if (font.outline)
  1015.     {
  1016.       cbox.cx = cbox.cy = font.cbox_size;
  1017.       GpiSetCharBox (f->hpsClient, &cbox);
  1018.       f->cur_cbox_size = font.cbox_size;
  1019.     }
  1020.   GpiQueryFontMetrics (f->hpsClient, (LONG)sizeof (fm), &fm);
  1021.   f->cxChar = fm.lMaxCharInc;
  1022.   f->cyChar = fm.lMaxBaselineExt;
  1023.   f->cyDesc = fm.lMaxDescender;
  1024.   for (i = 0; i < 512; ++i)
  1025.     f->increments[i] = fm.lMaxCharInc;
  1026. }
  1027.  
  1028.  
  1029. /* Add a color to the color table. */
  1030.  
  1031. #ifdef USE_PALETTE_MANAGER
  1032. static void add_color (COLOR color)
  1033. {
  1034.   int i;
  1035.  
  1036.   for (i = 0; i < colors; ++i)
  1037.     if (color_table[i] == color)
  1038.       return;
  1039.   color_table[colors++] = color;
  1040. }
  1041. #endif
  1042.  
  1043.  
  1044. /* Create palette.  Call this after adding a color. */
  1045.  
  1046. #ifdef USE_PALETTE_MANAGER
  1047. static void make_palette (frame_data *f)
  1048. {
  1049.   LONG size;
  1050.  
  1051.   if (has_palette_manager)
  1052.     {
  1053.       if (f->hpal != 0)
  1054.         GpiDeletePalette (f->hpal);
  1055.       f->hpal = GpiCreatePalette (hab, LCOL_PURECOLOR, LCOLF_CONSECRGB,
  1056.                                   colors, color_table);
  1057.       if (f->hpal == 0 || f->hpal == GPI_ERROR)
  1058.         pm_message (HWND_DESKTOP, "GpiCreatePalette failed");
  1059.       if (GpiSelectPalette (f->hpsClient, f->hpal) == PAL_ERROR)
  1060.         pm_message (HWND_DESKTOP, "GpiSelectPalette failed");
  1061.       if (WinRealizePalette (f->hwndClient, f->hpsClient, &size) == PAL_ERROR)
  1062.         pm_message (HWND_DESKTOP, "WinRealizePalette failed");
  1063.     }
  1064. }
  1065. #endif
  1066.  
  1067.  
  1068. /* Change the size of a frame.  The new size is taken from the WIDTH
  1069.    and HEIGHT fields of the frame data structure.  This function is
  1070.    also called to resize the PM window if the font has changed. */
  1071.  
  1072. static void set_size (frame_data *f)
  1073. {
  1074.   RECTL rcl;
  1075.   SWP swp;
  1076.   POINTL ptl;
  1077.  
  1078.   /* Don't call WinSetWindowPos if the frame is minimized or not yet
  1079.      completely created. */
  1080.  
  1081.   if (f->minimized || f->stage < STAGE_READY)
  1082.     return;
  1083.  
  1084.   /* Compute the rectangle of the frame window from the current
  1085.      position and the new size of the client window.  The upper left
  1086.      corner of the window is kept in place. */
  1087.  
  1088.   WinQueryWindowPos (f->hwndClient, &swp);
  1089.   ptl.x = swp.x;                /* Upper left-hand corner */
  1090.   ptl.y = swp.y + swp.cy;
  1091.   WinMapWindowPoints (f->hwndFrame, HWND_DESKTOP, &ptl, 1);
  1092.   rcl.xLeft = ptl.x;
  1093.   rcl.xRight = rcl.xLeft + (f->width + f->sb_width) * f->cxChar;
  1094.   rcl.yTop = ptl.y;
  1095.   rcl.yBottom = rcl.yTop - f->height * f->cyChar;
  1096.   WinCalcFrameRect (f->hwndFrame, &rcl, FALSE);
  1097.  
  1098.   /* When keeping the lower left-hand corner in place it might happen
  1099.      that the titlebar is moved out of the screen.  If this happens,
  1100.      move the window to keep the titlebar visible. */
  1101.  
  1102.   if (rcl.yTop > swpScreen.cy)
  1103.     {
  1104.       LONG offset = rcl.yTop - swpScreen.cy;
  1105.       rcl.yBottom -= offset;
  1106.       rcl.yTop -= offset;
  1107.     }
  1108.  
  1109.   /* Now resize and move the window. */
  1110.  
  1111.   WinSetWindowPos (f->hwndFrame, HWND_TOP, rcl.xLeft, rcl.yBottom,
  1112.                    rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
  1113.                    SWP_SIZE | SWP_MOVE);
  1114. }
  1115.  
  1116.  
  1117. /* Report the size of frame F to Emacs using the cxClient and cyClient
  1118.    fields. */
  1119.  
  1120. static void report_size (frame_data *f)
  1121. {
  1122.   pm_event pme;
  1123.  
  1124.   pme.size.header.type = PME_SIZE;
  1125.   pme.size.header.frame = f->id;
  1126.   pme.size.width = f->cxClient / f->cxChar - f->sb_width;
  1127.   pme.size.height = f->cyClient / f->cyChar;
  1128.   if (pme.size.width < 1) pme.size.width = 1;
  1129.   if (pme.size.height < 1) pme.size.height = 1;
  1130.   pme.size.pix_width = pme.size.width; /* TODO */
  1131.   pme.size.pix_height = pme.size.height; /* TODO */
  1132.   f->width = pme.size.width;
  1133.   f->height = pme.size.height;
  1134.   send_event (&pme);
  1135. }
  1136.  
  1137.  
  1138. /* Show the frame F for the first time. */
  1139.  
  1140. static void initial_show_frame (frame_data *f)
  1141. {
  1142.   LONG cx, cy;
  1143.   RECTL rcl;
  1144.   POINTL ptl;
  1145.   SWP swp;
  1146.   menubar_data mbd;
  1147.  
  1148.   /* Avoid recursion. */
  1149.  
  1150.   if (f->stage >= STAGE_GETTING_READY)
  1151.     return;
  1152.   f->stage = STAGE_GETTING_READY;
  1153.  
  1154.   /* First compute the size of the frame window. */
  1155.  
  1156.   cx = (f->width + f->sb_width) * f->cxChar;
  1157.   cy = f->height * f->cyChar;
  1158.   rcl.xLeft = 0; rcl.yBottom = 0;
  1159.   rcl.xRight = rcl.xLeft + cx;
  1160.   rcl.yTop = rcl.yBottom + cy;
  1161.   WinCalcFrameRect (f->hwndFrame, &rcl, FALSE);
  1162.  
  1163.   WinSetWindowPos (f->hwndFrame, NULLHANDLE, 0, 0,
  1164.                    rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
  1165.                    SWP_SIZE);
  1166.  
  1167.   /* Next, set the position of the frame window by computing the
  1168.      position of the client window in rcl.xLeft and rcl.yBottom,
  1169.      computing the frame window position from the client window
  1170.      position, and moving the frame window. */
  1171.  
  1172.   if (f->initial_left_base == 0 || f->initial_top_base == 0)
  1173.     {
  1174.       /* Unfortunately, FCF_SHELLPOSITION does not take effect until
  1175.          the window is made visible.  That is, we have to show the
  1176.          window to be able to obtain the position. */
  1177.  
  1178.       WinSetWindowPos (f->hwndFrame, HWND_TOP, 0, 0, 0, 0,
  1179.                        SWP_SHOW | SWP_ZORDER);
  1180.       WinQueryWindowPos (f->hwndClient, &swp);
  1181.       ptl.x = swp.x; /* Lower left-hand corner */
  1182.       ptl.y = swp.y;
  1183.       WinMapWindowPoints (f->hwndFrame, HWND_DESKTOP, &ptl, 1);
  1184.       rcl.xLeft = ptl.x;
  1185.       rcl.yBottom = ptl.y;
  1186.     }
  1187.  
  1188.   if (f->initial_left_base > 0)
  1189.     rcl.xLeft = f->initial_left;
  1190.   else if (f->initial_left_base < 0)
  1191.     rcl.xLeft = swpScreen.cx - cx + f->initial_left;
  1192.  
  1193.   if (f->initial_top_base > 0)
  1194.     rcl.yBottom = swpScreen.cy - 1 - (f->initial_top + cy);
  1195.   else if (f->initial_top_base < 0)
  1196.     rcl.yBottom = -f->initial_top;
  1197.  
  1198.   rcl.xRight = rcl.xLeft + cx;
  1199.   rcl.yTop = rcl.yBottom + cy;
  1200.   WinCalcFrameRect (f->hwndFrame, &rcl, FALSE);
  1201.   WinSetWindowPos (f->hwndFrame, HWND_TOP,
  1202.                    rcl.xLeft, rcl.yBottom,
  1203.                    rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
  1204.                    SWP_MOVE | SWP_SIZE | SWP_SHOW | SWP_ZORDER);
  1205.   WinSetActiveWindow (HWND_DESKTOP, f->hwndFrame);
  1206.   f->cxClient = cx;
  1207.   f->cyClient = cy;
  1208.   f->stage = STAGE_READY;
  1209.   report_size (f);
  1210.  
  1211.   mbd.data = NULL; mbd.str = NULL; mbd.el_count = 0; mbd.str_bytes = 0;
  1212.   WinSendMsg (f->hwndClient, UWM_MENUBAR, MPFROMP (f), MPFROMP (&mbd));
  1213.  
  1214.   WinInvalidateRect (f->hwndClient, NULL, FALSE);
  1215. }
  1216.  
  1217.  
  1218. /* Send a list of supported code pages to Emacs. */
  1219.  
  1220. static void cplist (int serial)
  1221. {
  1222.   ULONG acp[32];
  1223.   ULONG n;
  1224.  
  1225.   n = WinQueryCpList (hab, sizeof (acp) / sizeof (acp[0]), acp);
  1226.   send_answer (serial, acp, n * sizeof (acp[0]), 0);
  1227. }
  1228.  
  1229.  
  1230. /* Set the code page of the message queue and store it into
  1231.    `code_page', for font creation.  Note that all fonts will be
  1232.    recreated as all frames will be redrawn.  If answer is true, send
  1233.    an answer of 0 (failure) or 1 (success) to Emacs. */
  1234.  
  1235. static void set_code_page (int cp, int serial, int answer)
  1236. {
  1237.   int ok;
  1238.  
  1239.   ok = WinSetCp (hmq, cp);
  1240.   if (ok)
  1241.     {
  1242.       code_page = cp;
  1243.       clear_fonts ();
  1244.     }
  1245.   if (answer)
  1246.     send_answer (serial, NULL, 0, ok);
  1247. }
  1248.  
  1249.  
  1250. /* Translate PM shift key bits to Emacs modifier bits. */
  1251.  
  1252. static int convert_modifiers (frame_data *f, int shift)
  1253. {
  1254.   int result;
  1255.  
  1256.   result = 0;
  1257.   if (shift & KC_ALT)
  1258.     result |= f->alt_modifier;
  1259.   if (shift & KC_CTRL)
  1260.     result |= ctrl_modifier;
  1261.   if (shift & KC_SHIFT)
  1262.     result |= shift_modifier;
  1263.   if (shift & KC_ALTGR)         /* KC_ALTGR is a mock modifier */
  1264.     result |= f->altgr_modifier;
  1265.   return (result);
  1266. }
  1267.  
  1268.  
  1269. /* Send a keyboard event to Emacs.  F is the frame to which the event
  1270.    is sent, TYPE is the event subtype (PMK_ASCII or PMK_VIRTUAL).
  1271.    CODE is the character code, virtual key code or the symbol.
  1272.    MODIFIERS contains PM keyboard status bits for the shift keys, REP
  1273.    is the repeat count.  If the key matches quit_char, a SIGINT signal
  1274.    is sent instead of a keyboard event. */
  1275.  
  1276. static void send_key (frame_data *f, pm_key_type type, int code,
  1277.                       int modifiers, int rep)
  1278. {
  1279.   pm_event pme;
  1280.   int i;
  1281.  
  1282.   if (type == PMK_ASCII && code == quit_char && modifiers == 0)
  1283.     kill (emacs_pid, SIGINT);
  1284.   else
  1285.     {
  1286.       pme.key.header.type = PME_KEY;
  1287.       pme.key.header.frame = f->id;
  1288.       pme.key.type = type;
  1289.       pme.key.code = code;
  1290.       pme.key.modifiers = convert_modifiers (f, modifiers);
  1291.       for (i = 0; i < rep; ++i)
  1292.         send_event (&pme);
  1293.     }
  1294. }
  1295.  
  1296.  
  1297. /* Send a mouse click event to Emacs. */
  1298.  
  1299. static void send_button (frame_data *f, int x, int y, int button,
  1300.                          int modifiers, unsigned long timestamp)
  1301. {
  1302.   pm_event pme;
  1303.  
  1304.   pme.button.header.type = PME_BUTTON;
  1305.   pme.button.header.frame = f->id;
  1306.   pme.button.button = button;
  1307.   pme.button.modifiers = modifiers;
  1308.   pme.button.x = charbox_x (f, x);
  1309.   pme.button.y = charbox_y (f, y);
  1310.   if (pme.button.x < 0) pme.button.x = 0;
  1311.   if (pme.button.y < 0) pme.button.y = 0;
  1312.   pme.button.timestamp = timestamp;
  1313.   send_event (&pme);
  1314. }
  1315.  
  1316.  
  1317. /* Create a cursor for frame F, according to the cursor_type,
  1318.    cursor_width, and cursor_blink fields of *F. */
  1319.  
  1320. static void create_cursor (frame_data *f)
  1321. {
  1322.   LONG cx, cy;
  1323.   ULONG flags;
  1324.  
  1325.   if (f->cxChar != 0 && f->cyChar != 0)
  1326.     {
  1327.       cx = f->cxChar;
  1328.       cy = f->cyChar;
  1329.       flags = CURSOR_SOLID;
  1330.       switch (f->cursor_type)
  1331.         {
  1332.         case CURSORTYPE_BAR:
  1333.           cx = f->cursor_width;
  1334.           break;
  1335.         case CURSORTYPE_FRAME:
  1336.           flags = CURSOR_FRAME;
  1337.           break;
  1338.         case CURSORTYPE_UNDERLINE:
  1339.           cy = 0;
  1340.           break;
  1341.         case CURSORTYPE_HALFTONE:
  1342.           flags = CURSOR_HALFTONE;
  1343.           break;
  1344.         case CURSORTYPE_BOX:
  1345.           break;
  1346.         }
  1347.       if (f->cursor_blink)
  1348.         flags |= CURSOR_FLASH;
  1349.       WinCreateCursor (f->hwndClient, f->cursor_x, f->cursor_y, cx, cy,
  1350.                        flags, NULL);
  1351.       if (f->cursor_on)
  1352.         WinShowCursor (f->hwndClient, TRUE);
  1353.     }
  1354. }
  1355.  
  1356.  
  1357. /* Return true iff F's client window (or one if its children such as a
  1358.    scrollbar) has the focus. */
  1359.  
  1360. static int has_focus (frame_data *f)
  1361. {
  1362.   HWND hwndFocus;
  1363.  
  1364.   hwndFocus = WinQueryFocus (HWND_DESKTOP);
  1365.   return (hwndFocus == f->hwndClient
  1366.           || WinQueryWindow (hwndFocus, QW_PARENT) == f->hwndClient);
  1367. }
  1368.  
  1369.  
  1370. /* Create a new cursor for the frame F if F has the input focus. */
  1371.  
  1372. static void set_cursor (frame_data *f)
  1373. {
  1374.   if (has_focus (f))
  1375.     {
  1376.       WinDestroyCursor (f->hwndClient);
  1377.       create_cursor (f);
  1378.     }
  1379. }
  1380.  
  1381.  
  1382. /* Set the font of menu HWNDMENU to NAME.  Trying to use a
  1383.    non-existing font selects the default font.  Therefore, we can use
  1384.    the empty string to switch to the default font.  This function
  1385.    should be called after adding all the menu items; otherwise the
  1386.    height of the menu items will be wrong.  For the same reason,
  1387.    setting the presentation parameter with WinCreateWindow does not
  1388.    work. */
  1389.  
  1390. static void set_menu_font (HWND hwndMenu, char *name)
  1391. {
  1392.   WinSetPresParam (hwndMenu, PP_FONTNAMESIZE, strlen (name) + 1, name);
  1393. }
  1394.  
  1395.  
  1396. /* Update the font for all menus of frame F. */
  1397.  
  1398. static void update_menu_font (frame_data *f)
  1399. {
  1400.   HWND hwnd;
  1401.  
  1402.   hwnd = WinWindowFromID (f->hwndFrame, FID_SYSMENU);
  1403.   if (hwnd != NULLHANDLE)
  1404.     set_menu_font (hwnd, f->menu_font);
  1405.   hwnd = WinWindowFromID (f->hwndFrame, FID_MENU);
  1406.   if (hwnd != NULLHANDLE)
  1407.     set_menu_font (hwnd, f->menu_font);
  1408. }
  1409.  
  1410.  
  1411. /* Create a menu and return the window handle.  HWNDOWNER will be the
  1412.    owner of the menu.  ID is the window ID. */
  1413.  
  1414. static HWND create_menu_handle (HWND hwndOwner, ULONG id)
  1415. {
  1416.   return (WinCreateWindow (hwndOwner, WC_MENU, "", 0, 0, 0, 0, 0,
  1417.                            hwndOwner, HWND_TOP, id, NULL, NULL));
  1418. }
  1419.  
  1420.  
  1421. /* Add the value SEL to `menubar_id_map' of frame F and return the
  1422.    index of the new entry. */
  1423.  
  1424. static int add_menubar_id_map (frame_data *f, int sel)
  1425. {
  1426.   int id;
  1427.  
  1428.   if (f->menubar_id_used >= f->menubar_id_allocated)
  1429.     {
  1430.       int *new_map, new_alloc;
  1431.  
  1432.       new_alloc = f->menubar_id_allocated + 1024; /* One page */
  1433.       new_map = allocate (new_alloc * sizeof (*new_map));
  1434.       if (f->menubar_id_map != NULL)
  1435.         {
  1436.           memcpy (new_map, f->menubar_id_map,
  1437.                   f->menubar_id_used * sizeof (*new_map));
  1438.           deallocate (f->menubar_id_map);
  1439.         }
  1440.       f->menubar_id_map = new_map;
  1441.       f->menubar_id_allocated = new_alloc;
  1442.     }
  1443.   id = f->menubar_id_used++;
  1444.   f->menubar_id_map[id] = sel;
  1445.   return (id);
  1446. }
  1447.  
  1448.  
  1449. static void create_submenu (frame_data *f, HWND hwndMenu, HWND hwndOwner,
  1450.                             pm_menu **p, char *str, int sub, int menubarp)
  1451. {
  1452.   MENUITEM mi;
  1453.  
  1454.   for (;;)
  1455.     switch ((*p)->type)
  1456.       {
  1457.       case PMMENU_END:
  1458.         return;
  1459.  
  1460.       case PMMENU_POP:
  1461.         ++(*p);
  1462.         return;
  1463.  
  1464.       case PMMENU_ITEM:
  1465.         if ((*p)[1].type == PMMENU_PUSH)
  1466.           {
  1467.             mi.hwndSubMenu = create_menu_handle (hwndOwner, 0);
  1468.             mi.afStyle = MIS_SUBMENU | MIS_TEXT;
  1469.             mi.iPosition = MIT_END;
  1470.             mi.afAttribute = 0;
  1471.             mi.hItem = 0;
  1472.             mi.id = submenu_id++;
  1473.             if (first_id == 0) first_id = mi.id;
  1474.             WinSendMsg (hwndMenu, MM_INSERTITEM, MPFROMP (&mi),
  1475.                         MPFROMP (str + (*p)->str_offset));
  1476.             (*p) += 2;
  1477.             create_submenu (f, mi.hwndSubMenu, hwndOwner, p, str, sub,
  1478.                             menubarp);
  1479.           }
  1480.         else
  1481.           {
  1482.             mi.iPosition = MIT_END;
  1483.             mi.afStyle = MIS_TEXT;
  1484.             mi.afAttribute = ((*p)->enable ? 0 : MIA_DISABLED);
  1485.             mi.hwndSubMenu = NULLHANDLE;
  1486.             mi.hItem = 0;
  1487.             if (menubarp)
  1488.               mi.id = IDM_MENUBAR + add_menubar_id_map (f, (*p)->item);
  1489.             else
  1490.               mi.id = IDM_MENU + (*p)->item;
  1491.             if (first_id == 0) first_id = mi.id;
  1492.             WinSendMsg (hwndMenu, MM_INSERTITEM, MPFROMP (&mi),
  1493.                         MPFROMP (str + (*p)->str_offset));
  1494.             ++(*p);
  1495.           }
  1496.         break;
  1497.  
  1498.       case PMMENU_SEP:
  1499.         mi.iPosition = MIT_END;
  1500.         mi.afStyle = MIS_SEPARATOR;
  1501.         mi.afAttribute = 0;
  1502.         mi.hwndSubMenu = NULLHANDLE;
  1503.         mi.hItem = 0;
  1504.         mi.id = 0;
  1505.         WinSendMsg (hwndMenu, MM_INSERTITEM, MPFROMP (&mi), NULL);
  1506.         ++(*p);
  1507.         break;
  1508.  
  1509.       case PMMENU_SUB:
  1510.         if (sub)
  1511.           return;
  1512.         mi.hwndSubMenu = create_menu_handle (hwndOwner, 0);
  1513.         mi.afStyle = MIS_SUBMENU | MIS_TEXT;
  1514.         mi.iPosition = MIT_END;
  1515.         mi.afAttribute = 0;
  1516.         mi.hItem = 0;
  1517.         mi.id = submenu_id++;
  1518.         if (first_id == 0) first_id = mi.id;
  1519.         WinSendMsg (hwndMenu, MM_INSERTITEM, MPFROMP (&mi),
  1520.                     MPFROMP (str + (*p)->str_offset));
  1521.         ++(*p);
  1522.         create_submenu (f, mi.hwndSubMenu, hwndOwner, p, str, TRUE, menubarp);
  1523.         break;
  1524.  
  1525.       default:
  1526.         abort ();
  1527.       }
  1528. }
  1529.  
  1530.  
  1531. /* Create a menu, adding submenus to HWNDMENU.  The owner of
  1532.    submenus will be HWNDOWNER. */
  1533.  
  1534. static void create_menu (frame_data *f, HWND hwndMenu, HWND hwndOwner,
  1535.                          pm_menu **p, char *str, int menubarp)
  1536. {
  1537.   f->popup_active = FALSE;
  1538.   first_id = 0;
  1539.   create_submenu (f, hwndMenu, hwndOwner, p, str, FALSE, menubarp);
  1540.   if (f->menu_font[0] != 0)
  1541.     set_menu_font (hwndMenu, f->menu_font);
  1542. }
  1543.  
  1544.  
  1545. /* Create a popup menu and return its window handle. */
  1546.  
  1547. static HWND create_popup_menu (frame_data *f, pm_menu *p, char *str)
  1548. {
  1549.   HWND hwnd;
  1550.  
  1551.   hwnd = create_menu_handle (f->hwndClient, 0);
  1552.   submenu_id = IDM_SUBMENU;
  1553.   create_menu (f, hwnd, f->hwndClient, &p, str, FALSE);
  1554.   f->popup_active = TRUE;
  1555.   return (hwnd);
  1556. }
  1557.  
  1558.  
  1559. /* Update a menu of the menubar. */
  1560.  
  1561. static int update_menu (frame_data *f, HWND hwnd, pm_menu **p, char *str,
  1562.                         pm_menu **p0, char *str0, int sub)
  1563. {
  1564.   MENUITEM mi;
  1565.   MRESULT mr;
  1566.   int id;
  1567.  
  1568.   for (;;)
  1569.     switch ((*p)->type)
  1570.       {
  1571.       case PMMENU_END:
  1572.         return (TRUE);
  1573.  
  1574.       case PMMENU_POP:
  1575.         ++(*p); ++(*p0);
  1576.         return (TRUE);
  1577.  
  1578.       case PMMENU_ITEM:
  1579.         if ((*p)[1].type == PMMENU_PUSH)
  1580.           {
  1581.             id = submenu_id++;
  1582.             mr = WinSendMsg (hwnd, MM_QUERYITEM,
  1583.                              MPFROM2SHORT (id, FALSE), MPFROMP (&mi));
  1584.             if (!SHORT1FROMMR (mr)) return (FALSE);
  1585.             if (strcmp (str + (*p)->str_offset, str0 + (*p0)->str_offset) != 0)
  1586.               WinSendMsg (hwnd, MM_SETITEMTEXT, MPFROMSHORT (id),
  1587.                           MPFROMP (str + (*p)->str_offset));
  1588.             (*p) += 2; (*p0) += 2;
  1589.             update_menu (f, mi.hwndSubMenu, p, str, p0, str0, sub);
  1590.           }
  1591.         else
  1592.           {
  1593.             id = IDM_MENUBAR + add_menubar_id_map (f, (*p)->item);
  1594.             if ((*p)->enable != (*p0)->enable)
  1595.               {
  1596.                 WinSendMsg (hwnd, MM_SETITEMATTR, MPFROM2SHORT (id, FALSE),
  1597.                             MPFROM2SHORT (MIA_DISABLED,
  1598.                                           (*p)->enable ? 0 : MIA_DISABLED));
  1599.               }
  1600.             if (strcmp (str + (*p)->str_offset, str0 + (*p0)->str_offset) != 0)
  1601.               WinSendMsg (hwnd, MM_SETITEMTEXT, MPFROMSHORT (id),
  1602.                           MPFROMP (str + (*p)->str_offset));
  1603.             ++(*p); ++(*p0);
  1604.           }
  1605.         break;
  1606.  
  1607.       case PMMENU_SEP:
  1608.         ++(*p); ++(*p0);
  1609.         break;
  1610.  
  1611.       case PMMENU_SUB:
  1612.         if (sub)
  1613.           return (TRUE);
  1614.         id = submenu_id++;
  1615.         mr = WinSendMsg (hwnd, MM_QUERYITEM,
  1616.                          MPFROM2SHORT (id, FALSE), MPFROMP (&mi));
  1617.         if (!SHORT1FROMMR (mr)) return (FALSE);
  1618.         if (strcmp (str + (*p)->str_offset, str0 + (*p0)->str_offset) != 0)
  1619.           WinSendMsg (hwnd, MM_SETITEMTEXT, MPFROMSHORT (id),
  1620.                       MPFROMP (str + (*p)->str_offset));
  1621.         ++(*p); ++(*p0);
  1622.         update_menu (f, mi.hwndSubMenu, p, str, p0, str0, TRUE);
  1623.         break;
  1624.  
  1625.       default:
  1626.         abort ();
  1627.       }
  1628. }
  1629.  
  1630.  
  1631. /* Update the menubar. */
  1632.  
  1633. static void create_menubar (frame_data *f, pm_menu *els, char *str,
  1634.                             int el_count, int str_bytes)
  1635. {
  1636.   HWND hwnd;
  1637.   MENUITEM mi;
  1638.   SWP swp;
  1639.   POINTL aptl[2];
  1640.   RECTL rcl;
  1641.   MRESULT mr;
  1642.   pm_menu *p, *p0;
  1643.   char *str0;
  1644.   int i;
  1645.  
  1646.   if (els == NULL)
  1647.     {
  1648.       if (f->initial_menubar_el_count == 0)
  1649.         return;
  1650.       els = f->cur_menubar_el;
  1651.       str = f->cur_menubar_str;
  1652.       el_count = f->initial_menubar_el_count;
  1653.       str_bytes = f->initial_menubar_str_bytes;
  1654.     }
  1655.   else if (f->stage < STAGE_READY)
  1656.     {
  1657.       f->initial_menubar_el_count = el_count;
  1658.       f->initial_menubar_str_bytes = str_bytes;
  1659.       goto update_cur_menubar;
  1660.     }
  1661.   /* Quickly check if anything changed. */
  1662.   else if (f->cur_menubar_el != NULL && f->cur_menubar_str != NULL
  1663.       && el_count <= f->cur_menubar_el_allocated)
  1664.     {
  1665.       if (str_bytes <= f->cur_menubar_str_allocated
  1666.           && memcmp (els, f->cur_menubar_el, el_count * sizeof (pm_menu)) == 0
  1667.           && memcmp (str, f->cur_menubar_str, str_bytes) == 0)
  1668.         {
  1669.           /* Nothing changed at all.  This is the most common case. */
  1670.  
  1671.           return;
  1672.         }
  1673.  
  1674.       /* Now check if everything is unchanged except for the `enabled'
  1675.          state and texts.  If only the `enabled' state and texts have
  1676.          changed, modifying the menubar in place is very simple.  If
  1677.          the title of any menubar item (top level menu) has changed,
  1678.          we rebuild the menubar. */
  1679.  
  1680.       p = els; p0 = f->cur_menubar_el; str0 = f->cur_menubar_str;
  1681.       for (i = 0; i < el_count && p->type == p0->type; ++i, ++p, ++p0)
  1682.         {
  1683.           if (p->type == PMMENU_TOP
  1684.               && strcmp (str + p->str_offset, str0 + p0->str_offset) != 0)
  1685.             break;
  1686.         }
  1687.       if (i >= el_count)
  1688.         {
  1689.           /* Only simple changes were made, the submenu tree is
  1690.              unchanged.  Update the menubar in place without deleting
  1691.              or creating windows. */
  1692.  
  1693.           f->menubar_id_used = 0;
  1694.           submenu_id = IDM_SUBMENU;
  1695.           p = els; p0 = f->cur_menubar_el; i = IDM_MENUBAR_TOP;
  1696.           while (p->type == PMMENU_TOP)
  1697.             {
  1698.               ++p; ++p0;
  1699.               mr = WinSendMsg (f->hwndMenubar, MM_QUERYITEM,
  1700.                                MPFROM2SHORT (i, FALSE), MPFROMP (&mi));
  1701.               ++i;
  1702.               if (!SHORT1FROMMR (mr))
  1703.                 {
  1704.                   DosBeep (100, 500); goto replace_menubar;
  1705.                 }
  1706.               if (!update_menu (f, mi.hwndSubMenu, &p, str, &p0, str0, FALSE))
  1707.                 {
  1708.                   DosBeep (600, 500); goto replace_menubar;
  1709.                 }
  1710.               if (p->type != PMMENU_END)
  1711.                 break;
  1712.               ++p; ++p0;        /* Skip over PMMENU_END record */
  1713.             }
  1714.           goto update_cur_menubar;
  1715.         }
  1716.     }
  1717.  
  1718. replace_menubar:
  1719.   WinQueryWindowPos (f->hwndClient, &swp);
  1720.   aptl[0].x = swp.x;
  1721.   aptl[0].y = swp.y;
  1722.   aptl[1].x = swp.x + swp.cx;
  1723.   aptl[1].y = swp.y + swp.cy;
  1724.   WinMapWindowPoints (f->hwndFrame, HWND_DESKTOP, aptl, 2);
  1725.   rcl.xLeft = aptl[0].x; rcl.yBottom = aptl[0].y;
  1726.   rcl.xRight = aptl[1].x; rcl.yTop = aptl[1].y;
  1727.  
  1728.   hwnd = WinWindowFromID (f->hwndFrame, FID_MENU);
  1729.   if (hwnd != NULLHANDLE)
  1730.     WinDestroyWindow (hwnd);
  1731.  
  1732.   hwnd = WinCreateWindow (f->hwndFrame, WC_MENU, "", MS_ACTIONBAR, 0, 0, 0, 0,
  1733.                           f->hwndFrame, HWND_TOP, FID_MENU, NULL, NULL);
  1734.   f->menubar_id_used = 0;
  1735.   submenu_id = IDM_SUBMENU;
  1736.   p = els; i = IDM_MENUBAR_TOP;
  1737.   while (p->type == PMMENU_TOP)
  1738.     {
  1739.       mi.iPosition = MIT_END;
  1740.       mi.afStyle = MIS_SUBMENU | MIS_TEXT;
  1741.       mi.hwndSubMenu = create_menu_handle (f->hwndFrame, 0);
  1742.       mi.afAttribute = 0;
  1743.       mi.hItem = 0;
  1744.       mi.id = i++;
  1745.       WinSendMsg (hwnd, MM_INSERTITEM, MPFROMP (&mi),
  1746.                   MPFROMP (str + p->str_offset));
  1747.       ++p;
  1748.       create_menu (f, mi.hwndSubMenu, f->hwndFrame, &p, str, TRUE);
  1749.       if (p->type != PMMENU_END)
  1750.         break;
  1751.       ++p;                      /* Skip over PMMENU_END record */
  1752.     }
  1753.   set_menu_font (hwnd, f->menu_font);
  1754.   f->ignore_wm_size = TRUE;
  1755.   WinSendMsg (f->hwndFrame, WM_UPDATEFRAME, MPFROMSHORT (FCF_MENU), 0);
  1756.   f->ignore_wm_size = FALSE;
  1757.  
  1758.   WinCalcFrameRect (f->hwndFrame, &rcl, FALSE);
  1759.   if (rcl.yTop > swpScreen.cy)
  1760.     {
  1761.       LONG offset = rcl.yTop - swpScreen.cy;
  1762.       rcl.yBottom -= offset;
  1763.       rcl.yTop -= offset;
  1764.     }
  1765.   WinSetWindowPos (f->hwndFrame, HWND_TOP, rcl.xLeft, rcl.yBottom,
  1766.                    rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
  1767.                    SWP_MOVE | SWP_SIZE);
  1768.   f->hwndMenubar = hwnd;
  1769.  
  1770. update_cur_menubar:
  1771.   if (el_count > f->cur_menubar_el_allocated)
  1772.     {
  1773.       deallocate (f->cur_menubar_el);
  1774.       f->cur_menubar_el = allocate (el_count * sizeof (pm_menu));
  1775.       f->cur_menubar_el_allocated = el_count;
  1776.     }
  1777.   memmove (f->cur_menubar_el, els, el_count * sizeof (pm_menu));
  1778.   if (str_bytes > f->cur_menubar_str_allocated)
  1779.     {
  1780.       deallocate (f->cur_menubar_str);
  1781.       f->cur_menubar_str = allocate (str_bytes);
  1782.       f->cur_menubar_str_allocated = str_bytes;
  1783.     }
  1784.   memmove (f->cur_menubar_str, str, str_bytes);
  1785. }
  1786.  
  1787.  
  1788. /* Remove all items of the system menu of HWND, except for items ID1
  1789.    and ID2. */
  1790.  
  1791. static void sysmenu_remove_all_but (HWND hwnd, SHORT id1, SHORT id2)
  1792. {
  1793.   HWND hwndSysMenu;
  1794.   MENUITEM mi;
  1795.   SHORT i, id;
  1796.   MRESULT mr;
  1797.  
  1798.   hwndSysMenu = WinWindowFromID (hwnd, FID_SYSMENU);
  1799.   if (hwndSysMenu == NULLHANDLE)
  1800.     return;
  1801.   mr = WinSendMsg (hwndSysMenu, MM_QUERYITEM,
  1802.                    MPFROM2SHORT (SC_SYSMENU, FALSE), (MPARAM)&mi);
  1803.   if (!SHORT1FROMMR (mr) || mi.hwndSubMenu == NULLHANDLE)
  1804.     return;
  1805.   hwndSysMenu = mi.hwndSubMenu;
  1806.   mr = WinSendMsg (hwndSysMenu, MM_QUERYITEMCOUNT,
  1807.                    MPFROMSHORT (0), MPFROMSHORT (0));
  1808.   for (i = SHORT1FROMMR (mr); i >= 0; --i)
  1809.     {
  1810.       mr = WinSendMsg (hwndSysMenu, MM_ITEMIDFROMPOSITION,
  1811.                        MPFROMSHORT (i), MPFROMSHORT (0));
  1812.       id = SHORT1FROMMR (mr);
  1813.       if (id != MID_ERROR && id != id1 && id != id2)
  1814.         WinSendMsg (hwndSysMenu, MM_DELETEITEM,
  1815.                     MPFROM2SHORT (id, FALSE), 0L);
  1816.     }
  1817. }
  1818.  
  1819.  
  1820. /* Dialog procedure for dialog boxes created by create_dialog(). */
  1821.  
  1822. static MRESULT DlgProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1823. {
  1824.   int i;
  1825.  
  1826.   switch (msg)
  1827.     {
  1828.     case WM_INITDLG:
  1829.  
  1830.       /* SAA/CUA 89 requires all non-available menu choices being
  1831.          removed (from the system menu).  For dialog boxes, only the
  1832.          Move and Close choices remain.  As we don't want to have to
  1833.          handle the Close choice, remove all choices but the Move
  1834.          choice. */
  1835.  
  1836.       sysmenu_remove_all_but (hwnd, (SHORT)SC_MOVE, (SHORT)SC_MOVE);
  1837.  
  1838.       /* Setting WS_DISABLED in the dialog template makes the controls
  1839.          invisible, therefore we disable disabled buttons now. */
  1840.  
  1841.       for (i = 0; i < sizeof (disabled_buttons); ++i)
  1842.         if (disabled_buttons[i])
  1843.           WinEnableWindow (WinWindowFromID (hwnd, i), FALSE);
  1844.  
  1845.       /* The focus hasn't changed. */
  1846.  
  1847.       return ((MRESULT)FALSE);
  1848.  
  1849.     case WM_COMMAND:
  1850.  
  1851.       /* A button was pressed.  Dissmiss the dialog, letting
  1852.          WinProcessDlg return the button number ID. */
  1853.  
  1854.       WinDismissDlg (hwnd, COMMANDMSG (&msg)->cmd);
  1855.       break;
  1856.     }
  1857.   return (WinDefDlgProc (hwnd, msg, mp1, mp2));
  1858. }
  1859.  
  1860.  
  1861. /* Compute the width of a string, in dialog box units. */
  1862.  
  1863. static int dialog_string_width (const char *s)
  1864. {
  1865.   size_t len;
  1866.   POINTL *pptl;
  1867.  
  1868.   /* Allocate an ARRAY of POINTL structures.  There's one POINTL
  1869.      structure for each character, and one for the position after the
  1870.      last character. */
  1871.  
  1872.   len = strlen (s);
  1873.   if (len == 0)
  1874.     return (0);
  1875.   pptl = alloca ((len + 1) * sizeof (POINTL));
  1876.  
  1877.   /* Compute the positions of the characters. */
  1878.  
  1879.   if (!GpiQueryCharStringPos (hpsScreen, 0, len, s, NULL, pptl))
  1880.     return (0);
  1881.  
  1882.   /* Compute the distance between the X position after drawing the
  1883.      last character and initial X position.  Convert the distance to
  1884.      dialog box units. */
  1885.  
  1886.   return (PIX_TO_DLGUNIT_X (pptl[len].x - pptl[0].x));
  1887. }
  1888.  
  1889.  
  1890. /* Create a dialog and return its window handle. */
  1891.  
  1892. static HWND create_dialog (frame_data *f, int buttons, pm_menu *p, char *str)
  1893. {
  1894.   HWND hwnd;
  1895.   PDLGTEMPLATE pdlgt;
  1896.   DLGTITEM *item, *dialog_item, *msg_item = NULL;
  1897.   USHORT cb;
  1898.   LONG dialog_width, dialog_height; /* Pixels */
  1899.   ULONG ctldata;
  1900.   int i, items, x, cx;
  1901.   char *tpl, *s;
  1902.   char *title = "Emacs";
  1903.   char *msg = NULL;
  1904.  
  1905.   /* If there is a PMMENU_TITLE entry, it's the first entry. */
  1906.  
  1907.   if (p->type == PMMENU_TITLE)
  1908.     {
  1909.       msg = str + p->str_offset;
  1910.       ++p;
  1911.     }
  1912.  
  1913.   /* Compute the number of items in the DLGTEMPLATE. */
  1914.  
  1915.   items = buttons + 1;          /* DIALOG, plus one PUSHBUTTON per button */
  1916.   if (msg != NULL)
  1917.     items += 2;                 /* ICON, LTEXT */
  1918.  
  1919.   /* Compute the size of the fixed part of the template. */
  1920.  
  1921.   cb = sizeof (DLGTEMPLATE) + (items - 1) * sizeof (DLGTITEM);
  1922.  
  1923.   /* Allocate a buffer for the template, providing plenty space for
  1924.      the variable part (strings etc). */
  1925.  
  1926.   tpl = alloca (cb + 8192);
  1927.  
  1928.   /* Fill-in the values for DLGTEMPLATE. */
  1929.  
  1930.   pdlgt = (PDLGTEMPLATE)tpl;
  1931.   pdlgt->type = 0;              /* Only format 0 is defined */
  1932.   pdlgt->codepage = DIALOG_CODE_PAGE;
  1933.   pdlgt->offadlgti = offsetof (DLGTEMPLATE, adlgti);
  1934.   pdlgt->fsTemplateStatus = 0;  /* Reserved, must be 0 */
  1935.   pdlgt->iItemFocus = (USHORT)(-1);
  1936.   pdlgt->coffPresParams = 0;
  1937.  
  1938.   item = pdlgt->adlgti;
  1939.  
  1940.   /* Add a DIALOG `control'.  Only this control has children.  The x,
  1941.      y, and cx members are set further down, when the width of the
  1942.      dialog is known. */
  1943.  
  1944.   dialog_item = item++;
  1945.   dialog_item->fsItemStatus = 0; /* Reserved, must be 0 */
  1946.   dialog_item->cChildren = items - 1; /* All but DIALOG */
  1947.   dialog_item->cchClassName = 0;
  1948.   dialog_item->offClassName = (USHORT)(ULONG)WC_FRAME & 0xffff;
  1949.   dialog_item->cchText = strlen (title);
  1950.   dialog_item->offText = cb;
  1951.   strcpy (tpl + cb, title); cb += strlen (title) + 1;
  1952.   dialog_item->flStyle = WS_CLIPSIBLINGS | WS_SAVEBITS | FS_DLGBORDER;
  1953.   dialog_item->cy = 44;  /* TODO */
  1954.   dialog_item->id = 0;
  1955.   dialog_item->offPresParams = (USHORT)(-1);
  1956.   dialog_item->offCtlData = cb;
  1957.  
  1958.   ctldata = FCF_TITLEBAR | FCF_SYSMENU;
  1959.   memcpy (tpl + cb, &ctldata, sizeof (ctldata)); cb += sizeof (ctldata);
  1960.  
  1961.   /* Now come the controls to be placed in the DIALOG.  Children
  1962.      follow the parent in the array of DLGTITEMs. */
  1963.  
  1964.   /* If a message is provided, add controls for the icon (SYSICON) and
  1965.      the message (LTEXT). */
  1966.  
  1967.   if (msg != NULL)
  1968.     {
  1969.       char *icon = "#12";       /* SPTR_ICONQUESTION */
  1970.  
  1971.       x = 10;
  1972.  
  1973.       item->fsItemStatus = 0;
  1974.       item->cChildren = 0;
  1975.       item->cchClassName = 0;
  1976.       item->offClassName = (USHORT)(ULONG)WC_STATIC & 0xffff;
  1977.       item->cchText = strlen (icon);
  1978.       item->offText = cb;
  1979.       strcpy (tpl + cb, icon); cb += strlen (icon) + 1;
  1980.       item->flStyle = WS_VISIBLE | SS_SYSICON;
  1981.       item->x = x;
  1982.       item->y = 28;
  1983.       item->cx = 0;
  1984.       item->cy = 0;
  1985.       item->id = 0;
  1986.       item->offPresParams = (USHORT)(-1);
  1987.       item->offCtlData = (USHORT)(-1);
  1988.       ++item;
  1989.       x += PIX_TO_DLGUNIT_X (cxIcon) + 8;
  1990.  
  1991.       cx = dialog_string_width (msg);
  1992.       msg_item = item++;
  1993.       msg_item->fsItemStatus = 0;
  1994.       msg_item->cChildren = 0;
  1995.       msg_item->cchClassName = 0;
  1996.       msg_item->offClassName = (USHORT)(ULONG)WC_STATIC & 0xffff;
  1997.       msg_item->cchText = strlen (msg);
  1998.       msg_item->offText = cb;
  1999.       strcpy (tpl + cb, msg); cb += strlen (msg) + 1;
  2000.       msg_item->flStyle = WS_VISIBLE | WS_GROUP | SS_TEXT;
  2001.       msg_item->x = x;
  2002.       msg_item->y = 28;
  2003.       msg_item->cx = cx + 4;
  2004.       msg_item->cy = 8;
  2005.       msg_item->id = 0;
  2006.       msg_item->offPresParams = (USHORT)(-1);
  2007.       msg_item->offCtlData = (USHORT)(-1);
  2008.     }
  2009.  
  2010.   /* Add the BUTTON controls. */
  2011.  
  2012.   i = 0; x = 10;
  2013.   memset (disabled_buttons, 0, sizeof (disabled_buttons));
  2014.   while (p->type != PMMENU_END)
  2015.     {
  2016.       switch (p->type)
  2017.         {
  2018.         case PMMENU_ITEM:
  2019.           s = str + p->str_offset;
  2020.           cx = dialog_string_width (s) + 4;
  2021.           if (cx < 32) cx = 32;
  2022.           item->fsItemStatus = 0;
  2023.           item->cChildren = 0;
  2024.           item->cchClassName = 0;
  2025.           item->offClassName = (USHORT)(ULONG)WC_BUTTON & 0xffff;
  2026.           item->cchText = strlen (s);
  2027.           item->offText = cb;
  2028.           strcpy (tpl + cb, s); cb += strlen (s) + 1;
  2029.           item->flStyle = (WS_VISIBLE | WS_GROUP | WS_TABSTOP | BS_AUTOSIZE);
  2030.  
  2031.           item->x = x;
  2032.           item->y = 8;
  2033.           item->cx = cx;
  2034.           item->cy = 12;
  2035.           item->id = p->item;
  2036.           item->offPresParams = (USHORT)(-1);
  2037.           item->offCtlData = (USHORT)(-1);
  2038.  
  2039.           if (!p->enable && p->item < sizeof (disabled_buttons))
  2040.             disabled_buttons[p->item] = 1;
  2041.  
  2042.           x += cx + 10;
  2043.           ++item; ++i;
  2044.           break;
  2045.  
  2046.         default:
  2047.           abort ();
  2048.         }
  2049.       ++p;
  2050.     }
  2051.   if (i != buttons)
  2052.     return (NULLHANDLE);
  2053.  
  2054.   /* Compute the width of the dialog. */
  2055.  
  2056.   cx = x;
  2057.   if (cx < 120)
  2058.     cx = 120;
  2059.   if (msg_item != NULL && cx < (msg_item->x + msg_item->cx + 4))
  2060.     cx = msg_item->x + msg_item->cx + 4;
  2061.  
  2062.   dialog_item->cx = cx;
  2063.  
  2064.   /* Center the dialog in the frame window. */
  2065.  
  2066.   dialog_width = DLGUNIT_TO_PIX_X (dialog_item->cx);
  2067.   dialog_height = DLGUNIT_TO_PIX_Y (dialog_item->cy);
  2068.   dialog_item->x = PIX_TO_DLGUNIT_X ((f->cxClient - dialog_width) / 2);
  2069.   dialog_item->y = PIX_TO_DLGUNIT_Y ((f->cyClient - dialog_height) / 2);
  2070.  
  2071.   /* Set the size of the structure. */
  2072.  
  2073.   pdlgt->cbTemplate = cb;
  2074.  
  2075.   /* Create the dialog window from the template. */
  2076.  
  2077.   hwnd = WinCreateDlg (HWND_DESKTOP, f->hwndClient, DlgProc, pdlgt, NULL);
  2078.   return (hwnd);
  2079. }
  2080.  
  2081.  
  2082. MRESULT FileDlgProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  2083. {
  2084.   switch (msg)
  2085.     {
  2086.     case FDM_ERROR:
  2087.       /* Let the file dialog display the error message.  */
  2088.       return (MRESULT)0;
  2089.  
  2090.     case FDM_FILTER:
  2091.       /* Don't omit any files.  */
  2092.       return (MRESULT)TRUE;
  2093.  
  2094.     case FDM_VALIDATE:
  2095.       /* Check validity.  */
  2096.       if (((PFILEDLG)WinQueryWindowULong (hwnd, QWL_USER))->ulUser)
  2097.         {
  2098.           DosError (FERR_DISABLEHARDERR | FERR_ENABLEEXCEPTION);
  2099.           if (access ((char *)PVOIDFROMMP (mp1), 0) != 0)
  2100.             {
  2101.               pm_message (hwnd, "File %s does not exist",
  2102.                           (char *)PVOIDFROMMP (mp1));
  2103.               return (MRESULT)FALSE;
  2104.             }
  2105.         }
  2106.       return (MRESULT)TRUE;
  2107.     }
  2108.   return WinDefFileDlgProc (hwnd, msg, mp1, mp2);
  2109. }
  2110.  
  2111.  
  2112. /* Display and process a file dialog.  */
  2113.  
  2114. static void file_dialog (frame_data *f, pm_filedialog *pdata)
  2115. {
  2116.   FILEDLG fdg;
  2117.   pm_filedialog data;
  2118.   char *p;
  2119.  
  2120.   data = *pdata;
  2121.   DosPostEventSem (hevDone);
  2122.  
  2123.   /* Select the default directory as current working directory.  */
  2124.  
  2125.   DosError (FERR_DISABLEHARDERR | FERR_ENABLEEXCEPTION);
  2126.   _chdir2 (data.dir);
  2127.  
  2128.   /* Initialize the FILEDLG structure.  */
  2129.  
  2130.   fdg.cbSize = sizeof (FILEDLG);
  2131.   fdg.pszTitle = data.title;
  2132.   fdg.pszOKButton = data.ok_button;
  2133.   fdg.ulUser = data.must_match;
  2134.   fdg.fl = (data.save_as ? (FDS_SAVEAS_DIALOG | FDS_ENABLEFILELB)
  2135.             : FDS_OPEN_DIALOG);
  2136.   fdg.fl |= FDS_CENTER;
  2137.   fdg.pfnDlgProc = FileDlgProc;
  2138.   fdg.lReturn = 0;
  2139.   fdg.lSRC = 0;
  2140.   fdg.hMod = 0;
  2141.   fdg.usDlgId = 0;
  2142.   fdg.x = 0;
  2143.   fdg.y = 0;
  2144.   strcpy (fdg.szFullFile, data.defalt);
  2145.   fdg.pszIType       = NULL;
  2146.   fdg.papszITypeList = NULL;
  2147.   fdg.pszIDrive      = NULL;
  2148.   fdg.papszIDriveList= NULL;
  2149.   fdg.sEAType        = 0;
  2150.   fdg.papszFQFilename= NULL;
  2151.   fdg.ulFQFCount     = 0;
  2152.  
  2153.   /* Process the dialog box.  */
  2154.  
  2155.   if (WinFileDlg (HWND_DESKTOP, f->hwndClient, &fdg)
  2156.       && fdg.lReturn == DID_OK)
  2157.     {
  2158.       for (p = fdg.szFullFile; *p != 0; ++p)
  2159.         if (*p == '\\')
  2160.           *p = '/';
  2161.       send_answer (data.serial, fdg.szFullFile, strlen (fdg.szFullFile), 0);
  2162.     }
  2163.   else
  2164.     send_answer (data.serial, "", 0, 0);
  2165. }
  2166.  
  2167.  
  2168. /* Capture the mouse. */
  2169.  
  2170. static void capture (frame_data *f, int yes)
  2171. {
  2172.   if (yes && !capture_flag)
  2173.     {
  2174.       WinSetCapture (HWND_DESKTOP, f->hwndClient);
  2175.       capture_flag = TRUE;
  2176.     }
  2177.   if (!yes && capture_flag)
  2178.     {
  2179.       WinSetCapture (HWND_DESKTOP, NULLHANDLE);
  2180.       capture_flag = FALSE;
  2181.     }
  2182. }
  2183.  
  2184.  
  2185. /* Mouse button pressed or released.  Compute the coordinates of the
  2186.    character cell, convert the modifier bits, and send to Emacs.  This
  2187.    function returns FALSE if the message is to be passed on to the
  2188.    default window procedure (which in turn passes it on to the frame
  2189.    window) because this window doesn't have the focus.  That way, the
  2190.    window is made active and the button message is not sent to Emacs.
  2191.    Moreover, WM_BUTTONxUP messages are ignored after making the window
  2192.    active until an appropriate WM_BUTTONxDOWN message is received. */
  2193.  
  2194. static int mouse_button (HWND hwnd, MPARAM mp1, MPARAM mp2, int button,
  2195.                          int modifier)
  2196. {
  2197.   frame_data *f;
  2198.  
  2199.   f = WinQueryWindowPtr (hwnd, 0);
  2200.   if (hwnd == WinQueryFocus (HWND_DESKTOP))
  2201.     {
  2202.       if (modifier == down_modifier)
  2203.         f->ignore_button_up &= ~(1 << button);
  2204.       if (f->buttons[button] != 0
  2205.           && (modifier == down_modifier
  2206.               || !(f->ignore_button_up & (1 << button))))
  2207.         {
  2208.           capture (f, modifier == down_modifier);
  2209.           modifier |= convert_modifiers (f, (USHORT)SHORT2FROMMP (mp2));
  2210.           send_button (f, (USHORT)SHORT1FROMMP (mp1),
  2211.                        (USHORT)SHORT2FROMMP (mp1),
  2212.                        f->buttons[button] - 1, modifier,
  2213.                        WinQueryMsgTime (hab));
  2214.           return (TRUE);
  2215.         }
  2216.     }
  2217.   else
  2218.     f->ignore_button_up = 7;
  2219.  
  2220.   /* Continue processing -- make window active */
  2221.   return (FALSE);
  2222. }
  2223.  
  2224.  
  2225. /* Send a BUTTON_DROP event to Emacs. */
  2226.  
  2227. static void send_drop (frame_data *f, LONG x, LONG y, int button,
  2228.                        const char *str, unsigned len)
  2229. {
  2230.   DosRequestMutexSem (drop_mutex, SEM_INDEFINITE_WAIT);
  2231.   drop_list[drop_in].cookie = drop_cookie;
  2232.   drop_list[drop_in].len = len;
  2233.   memcpy (drop_list[drop_in].str, str, len);
  2234.   if (++drop_in == DROP_MAX) drop_in = 0;
  2235.   ++drop_count;
  2236.   DosReleaseMutexSem (drop_mutex);
  2237.   send_button (f, x, y, button, 0, drop_cookie);
  2238.   ++drop_cookie;
  2239. }
  2240.  
  2241.  
  2242. /* An object is being dragged over the client window.  Check whether
  2243.    it can be dropped or not. */
  2244.  
  2245. MRESULT drag_over (PDRAGINFO pDraginfo)
  2246. {
  2247.   PDRAGITEM pditem;
  2248.   USHORT indicator, operation;
  2249.  
  2250. /* Redefine this macro for debugging. */
  2251. #define DRAG_FAIL(x) ((void)0)
  2252.  
  2253.   indicator = DOR_NODROPOP; operation = DO_COPY;
  2254.   if (!DrgAccessDraginfo (pDraginfo))
  2255.     DRAG_FAIL ("DrgAccessDraginfo failed");
  2256.   else if (!(pDraginfo->usOperation == DO_DEFAULT
  2257.              || pDraginfo->usOperation == DO_COPY))
  2258.     DRAG_FAIL ("Invalid operation");
  2259.   else if (DrgQueryDragitemCount (pDraginfo) < 1)
  2260.     DRAG_FAIL ("Invalid count");
  2261.   else if (DrgQueryDragitemCount (pDraginfo) > DROP_MAX - drop_count)
  2262.     DRAG_FAIL ("Circular buffer full");
  2263.   else
  2264.     {
  2265.       pditem = DrgQueryDragitemPtr (pDraginfo, 0);
  2266.       if (!(pditem->fsSupportedOps & DO_COPYABLE))
  2267.         DRAG_FAIL ("Not copyable");
  2268.       else if (!DrgVerifyRMF (pditem, "DRM_OS2FILE", NULL))
  2269.         DRAG_FAIL ("DrgVerifyRMF failed");
  2270.       else
  2271.         {
  2272.           /* The object can be dropped (copied). */
  2273.           indicator = DOR_DROP; operation = DO_COPY;
  2274.         }
  2275.     }
  2276.   DrgFreeDraginfo (pDraginfo);
  2277.   return (MRFROM2SHORT (indicator, operation));
  2278. }
  2279.  
  2280.  
  2281. /* An object is dropped on the client window. */
  2282.  
  2283. MRESULT drag_drop (HWND hwnd, PDRAGINFO pDraginfo)
  2284. {
  2285.   PDRAGITEM pditem;
  2286.   POINTL ptl;
  2287.   char name[CCHMAXPATH];
  2288.   char path[CCHMAXPATH];
  2289.   char *p;
  2290.   int count, idx, len;
  2291.   frame_data *f;
  2292.  
  2293.   f = WinQueryWindowPtr (hwnd, 0);
  2294.   DrgAccessDraginfo (pDraginfo);
  2295.   ptl.x = pDraginfo->xDrop; ptl.y = pDraginfo->yDrop;
  2296.   WinMapWindowPoints (HWND_DESKTOP, hwnd, &ptl, 1);
  2297.   count = DrgQueryDragitemCount (pDraginfo);
  2298.   for (idx = 0; idx < count && drop_count < DROP_MAX; ++idx)
  2299.     {
  2300.       pditem = DrgQueryDragitemPtr (pDraginfo, idx);
  2301.       DrgQueryStrName (pditem->hstrContainerName, sizeof (path), path);
  2302.       DrgQueryStrName (pditem->hstrSourceName, sizeof (name), name);
  2303.       DrgSendTransferMsg (pditem->hwndItem, DM_ENDCONVERSATION,
  2304.                           MPFROMLONG (pditem->ulItemID),
  2305.                           MPFROMSHORT (DMFL_TARGETSUCCESSFUL));
  2306.       len = strlen (path);
  2307.       if (len >= 1 && strchr ("\\/:", path[len-1]) == NULL)
  2308.         path[len++] = '/';
  2309.       if (len + strlen (name) + 1 <= sizeof (path))
  2310.         {
  2311.           strcpy (path + len, name);
  2312.           for (p = path; *p != 0; ++p)
  2313.             if (*p == '\\')
  2314.               *p = '/';
  2315.           send_drop (f, ptl.x, ptl.y, BUTTON_DROP_FILE, path, strlen (path));
  2316.         }
  2317.     }
  2318.   DrgDeleteDraginfoStrHandles (pDraginfo);
  2319.   DrgFreeDraginfo (pDraginfo);
  2320.   return (0);
  2321. }
  2322.  
  2323.  
  2324. /* Send to Emacs the name of the dropped object associated with
  2325.    COOKIE.  Discard all objects dropped before the one associated with
  2326.    COOKIE. */
  2327.  
  2328. static void get_drop (int cookie, int serial)
  2329. {
  2330.   struct drop d;
  2331.  
  2332.   d.str[0] = 0;
  2333.   DosRequestMutexSem (drop_mutex, SEM_INDEFINITE_WAIT);
  2334.   while (drop_count > 0)
  2335.     {
  2336.       if (drop_list[drop_out].cookie == cookie)
  2337.         {
  2338.           d = drop_list[drop_out];
  2339.           if (++drop_out == DROP_MAX) drop_out = 0;
  2340.           --drop_count;
  2341.           break;
  2342.         }
  2343.       if (drop_list[drop_out].cookie > cookie)
  2344.         break;
  2345.       if (++drop_out == DROP_MAX) drop_out = 0;
  2346.       --drop_count;
  2347.     }
  2348.   DosReleaseMutexSem (drop_mutex);
  2349.   send_answer (serial, d.str, d.len, 0);
  2350. }
  2351.  
  2352.  
  2353. /* A color has been dropped on a frame (or the background color has
  2354.    been changed with WinSetPresParam). */
  2355.  
  2356. static void drop_color (HWND hwnd)
  2357. {
  2358.   frame_data *f;
  2359.   RGB rgb;
  2360.   char buf[3];
  2361.  
  2362.   f = WinQueryWindowPtr (hwnd, 0);
  2363.   if (WinQueryPresParam (hwnd, PP_BACKGROUNDCOLOR, 0, NULL,
  2364.                          sizeof (rgb), &rgb,
  2365.                          QPF_NOINHERIT | QPF_PURERGBCOLOR) == sizeof (rgb))
  2366.     {
  2367.       buf[0] = rgb.bRed;
  2368.       buf[1] = rgb.bGreen;
  2369.       buf[2] = rgb.bBlue;
  2370.       send_drop (f, 0, 0, BUTTON_DROP_COLOR, buf, 3);
  2371.     }
  2372.  
  2373. }
  2374.  
  2375.  
  2376. /* Handle PMR_UPDATE_SCROLLBAR.  Derived from code written by Patrick
  2377.    Nadeau.  */
  2378.  
  2379. static void update_scrollbar (unsigned id, const slider *s)
  2380. {
  2381.   HWND hwndScrollBar;
  2382.  
  2383.   if (id >= MAX_SCROLLBARS)
  2384.     return;
  2385.  
  2386.   /* Don't update the slider while dragging. */
  2387.  
  2388.   if (dragging_scrollbar_slider)
  2389.     {
  2390.       scrollbar_table[id].async = *s;
  2391.       return;
  2392.     }
  2393.  
  2394.   hwndScrollBar = scrollbar_table[id].hwnd;
  2395.  
  2396.   WinSendMsg (hwndScrollBar, SBM_SETSCROLLBAR,
  2397.               MPFROMSHORT ((short)s->position),
  2398.               MPFROM2SHORT (0, (short)s->whole));
  2399.   WinSendMsg (hwndScrollBar, SBM_SETTHUMBSIZE,
  2400.               MPFROM2SHORT ((short)s->portion, (short)s->whole),
  2401.               0);
  2402.  
  2403.   scrollbar_table[id].range = (short)s->whole;
  2404. }
  2405.  
  2406.  
  2407. /* Handle PMR_MOVE_SCROLLBAR.  Derived from code written by Patrick
  2408.    Nadeau.  */
  2409.  
  2410. static void move_scrollbar (frame_data *f, unsigned id, int top, int left,
  2411.                             int width, int height)
  2412. {
  2413.   HWND hwndScrollBar;
  2414.   LONG x, y, cx, cy;
  2415.  
  2416.   /* Don't update the slider while dragging. */
  2417.  
  2418.   if (dragging_scrollbar_slider || id >= MAX_SCROLLBARS)
  2419.     return;
  2420.  
  2421.   hwndScrollBar = scrollbar_table[id].hwnd;
  2422.  
  2423.   x = make_x (f, left);
  2424.   y = make_y (f, top + height - 1) - f->cyDesc;
  2425.  
  2426.   cx = make_x (f, width) - make_x (f, 0);
  2427.   cy = make_y (f, 0) - make_y (f, height);
  2428.  
  2429.   WinSetWindowPos (hwndScrollBar, HWND_TOP, x, y, cx, cy,
  2430.                    SWP_SIZE | SWP_MOVE | SWP_SHOW);
  2431. }
  2432.  
  2433.  
  2434. /* Handle UWM_CREATE_SCROLLBAR.  Derived from code written by Patrick
  2435.    Nadeau.  */
  2436.  
  2437. static void create_scrollbar (frame_data *f, pm_create_scrollbar *psb)
  2438. {
  2439.   HWND hwndScrollBar = NULLHANDLE;
  2440.   pmd_create_scrollbar result;
  2441.   unsigned id;
  2442.  
  2443.   for (id = 0; id < MAX_SCROLLBARS; ++id)
  2444.     if (scrollbar_table[id].hwnd == NULLHANDLE)
  2445.       break;
  2446.   if (id >= MAX_SCROLLBARS)
  2447.     id = SB_INVALID_ID;
  2448.   else
  2449.     {
  2450.       hwndScrollBar = WinCreateWindow (f->hwndClient, WC_SCROLLBAR, "",
  2451.                                        SBS_VERT | WS_DISABLED,
  2452.                                        0, 0, 0, 0,
  2453.                                        f->hwndClient, HWND_TOP,
  2454.                                        FID_SCROLLBAR + id, NULL, NULL);
  2455.       if (hwndScrollBar == NULLHANDLE)
  2456.         id = SB_INVALID_ID;
  2457.       else
  2458.         {
  2459.           scrollbar_table[id].hwnd = hwndScrollBar;
  2460.           scrollbar_table[id].async.whole = -1;
  2461.           update_scrollbar (id, &psb->s);
  2462.           move_scrollbar (f, id, psb->top, psb->left, psb->width, psb->height);
  2463.         }
  2464.     }
  2465.  
  2466.   result.id = id;
  2467.   send_answer (psb->serial, &result, sizeof (result), 0);
  2468. }
  2469.  
  2470.  
  2471. /* Handle UWM_DESTROY_SCROLLBAR.  Derived from code written by Patrick
  2472.    Nadeau.  */
  2473.  
  2474. static void destroy_scrollbar (unsigned id)
  2475. {
  2476.   if (id >= MAX_SCROLLBARS)
  2477.     return;
  2478.   if (scrollbar_table[id].hwnd != NULLHANDLE)
  2479.     {
  2480.       WinDestroyWindow (scrollbar_table[id].hwnd);
  2481.       scrollbar_table[id].hwnd = NULLHANDLE;
  2482.     }
  2483. }
  2484.  
  2485.  
  2486. /* Handle WM_VSCROLL.  Originally written by Patrick Nadeau, modified
  2487.    by Eberhard Mattes. */
  2488.  
  2489. static void handle_vscroll (HWND hwnd, MPARAM mp1, MPARAM mp2)
  2490. {
  2491.   frame_data *f;
  2492.   int part;
  2493.   USHORT cmd;
  2494.   unsigned id;
  2495.   int slider_pos, slider_range;
  2496.   pm_event pme;
  2497.  
  2498.   id = SHORT1FROMMP (mp1) - FID_SCROLLBAR;
  2499.   if (id >= MAX_SCROLLBARS || scrollbar_table[id].hwnd == NULLHANDLE)
  2500.     return;
  2501.  
  2502.   slider_pos = SHORT1FROMMP (mp2);
  2503.   cmd = SHORT2FROMMP (mp2);
  2504.  
  2505.   /* PM always gives a slider pos of 0 for events SB_PAGEDOWN,
  2506.      SB_PAGEUP, SB_LINEDOWN and SB_LINEUP.
  2507.  
  2508.      When this happens we set both the range and the slider pos to 0
  2509.      to indicate that the pair is not valid for computing buffer
  2510.      positions.
  2511.  
  2512.      The only message that does give a valid slider pos is
  2513.      SB_SLIDERPOSITION (or SB_SLIDERTRACK) in which case we do set the
  2514.      pair to the slider pos and range.
  2515.  
  2516.      Hence we set the default slider_range to 0 here.  */
  2517.       
  2518.   slider_range = 0;
  2519.  
  2520.   /* Avoid updating the slider position and size while dragging.  */
  2521.  
  2522.   if (cmd == SB_SLIDERTRACK && !dragging_scrollbar_slider)
  2523.     {
  2524.       dragging_scrollbar_slider = TRUE;
  2525.       scrollbar_table[id].async.whole = -1;
  2526.     }
  2527.   else if (cmd != SB_SLIDERTRACK && dragging_scrollbar_slider)
  2528.     {
  2529.       dragging_scrollbar_slider = FALSE;
  2530.       if (scrollbar_table[id].async.whole != -1)
  2531.         update_scrollbar (id, &scrollbar_table[id].async);
  2532.     }
  2533.  
  2534.   /* We only generate events for the cases below.  While we are at it
  2535.      we figure out what the scroll bar part should be. */
  2536.  
  2537.   switch (cmd)
  2538.     {
  2539.     case SB_PAGEDOWN:
  2540.       part = scroll_bar_below_handle;
  2541.       break;
  2542.     case SB_PAGEUP:
  2543.       part = scroll_bar_above_handle;
  2544.       break;
  2545.     case SB_LINEDOWN:
  2546.       part = scroll_bar_down_arrow;
  2547.       break;
  2548.     case SB_LINEUP:
  2549.       part = scroll_bar_up_arrow;
  2550.       break;
  2551.     case SB_SLIDERPOSITION:
  2552.     case SB_SLIDERTRACK:
  2553.       part = scroll_bar_handle;
  2554.       slider_range = scrollbar_table[id].range;
  2555.       break;
  2556.     default:
  2557.       return;
  2558.     }
  2559.  
  2560.   f = WinQueryWindowPtr (hwnd, 0);
  2561.  
  2562.   pme.scrollbar.header.type = PME_SCROLLBAR;
  2563.   pme.scrollbar.header.frame = f->id;
  2564.  
  2565.   pme.button.modifiers = up_modifier;
  2566.   pme.scrollbar.button = f->buttons[0]-1; /* BUTTON1 in PM */
  2567.   pme.scrollbar.part = part;
  2568.   pme.scrollbar.x = slider_pos;
  2569.   pme.scrollbar.y = slider_range;
  2570.   pme.scrollbar.id = id;
  2571.   pme.scrollbar.timestamp = WinQueryMsgTime (hab);
  2572.   send_event (&pme);
  2573. }
  2574.  
  2575.  
  2576. /* Client window procedure for the Emacs frames.  This function is
  2577.    called in the PM thread. */
  2578.  
  2579. static MRESULT ClientWndProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  2580. {
  2581.   HPS hps;
  2582.   HDC hdc;
  2583.   RECTL rcl, rclBound;
  2584.   POINTL ptl;
  2585.   SIZEL sizel;
  2586.   SWP swp;
  2587.   ULONG options, cmd;
  2588.   HWND hwndDlg;
  2589.   PQMSG pqmsg;
  2590.   unsigned fsflags, usch, usvk, usrep, ussc, bit;
  2591.   pm_event pme;
  2592.   frame_data *f;
  2593.   menu_data *md;
  2594.   menubar_data *mbd;
  2595.   int i, dlg_answer;
  2596.  
  2597.   switch (msg)
  2598.     {
  2599.     case WM_CREATE:
  2600.  
  2601.       /* Creating the client window.  The pointer to the frame_data
  2602.          structure is passed in a global variable as the window is
  2603.          create by WinCreateStdWindow.  Save the pointer in the window
  2604.          data area. */
  2605.  
  2606.       f = new_frame;
  2607.       WinSetWindowPtr (hwnd, 0, f);
  2608.  
  2609.       /* Setup the fields of the frame_data structure. */
  2610.  
  2611.       f->hwndClient = hwnd;
  2612.       f->hwndPopupMenu = NULLHANDLE;
  2613.       f->hwndMenubar = NULLHANDLE;
  2614.       hdc = WinOpenWindowDC (hwnd);
  2615.       sizel.cx = sizel.cy = 0;
  2616.       f->hpsClient = GpiCreatePS (hab, hdc, &sizel,
  2617.                                   PU_PELS | GPIT_MICRO | GPIA_ASSOC);
  2618.  
  2619.       /* Switch color table to RGB mode.  This will be overriden by
  2620.          make_palette() if the palette manager is used. */
  2621.  
  2622.       GpiCreateLogColorTable (f->hpsClient, LCOL_PURECOLOR, LCOLF_RGB,
  2623.                               0, 0, NULL);
  2624.  
  2625. #ifdef USE_PALETTE_MANAGER
  2626.       f->hpal = 0;
  2627.       make_palette (f);
  2628. #endif
  2629.  
  2630.       /* Select the default font and get the font metrics. */
  2631.  
  2632.       set_default_font (f);
  2633.  
  2634.       /* Put current attributes into cache. */
  2635.       f->cur_color = GpiQueryColor (f->hpsClient);
  2636.       f->cur_backcolor = GpiQueryBackColor (f->hpsClient);
  2637.       f->cur_backmix = GpiQueryBackMix (f->hpsClient);
  2638.       f->cur_charset = GpiQueryCharSet (f->hpsClient);
  2639.       f->cur_cbox_size = 0;
  2640.       return (0);
  2641.  
  2642.     case WM_DESTROY:
  2643. #ifdef USE_PALETTE_MANAGER
  2644.       if (has_palette_manager)
  2645.         {
  2646.           GpiSelectPalette (f->hpsClient, NULLHANDLE);
  2647.           GpiDeletePalette (f->hpal);
  2648.         }
  2649. #endif
  2650.       break;
  2651.  
  2652.     case WM_PAINT:
  2653.  
  2654.       /* The client window needs repainting.  Let Emacs redraw the
  2655.          frame.  Here, we only erase the border of partial character
  2656.          boxes which may surround the Emacs frame.  When maximizing a
  2657.          window or when calling WinSetWindowPos() in another program,
  2658.          the window may be slightly larger than required, leaving a
  2659.          border at the right and bottom edges.  */
  2660.  
  2661.       f = WinQueryWindowPtr (hwnd, 0);
  2662.       hps = WinBeginPaint (hwnd, NULLHANDLE, &rclBound);
  2663.       if (f->stage < STAGE_READY)
  2664.         {
  2665.           WinEndPaint (hps);
  2666.           return (0);
  2667.         }
  2668.       GpiCreateLogColorTable (hps, LCOL_PURECOLOR, LCOLF_RGB, 0, 0, NULL);
  2669.       rcl.xLeft = 0; rcl.xRight = f->width * f->cxChar - 1;
  2670.       rcl.yTop = f->cyClient; rcl.yBottom = rcl.yTop - f->height * f->cyChar;
  2671.       GpiExcludeClipRectangle (hps, &rcl);
  2672.       WinQueryWindowRect (hwnd, &rcl);
  2673.       WinFillRect (hps, &rcl, GET_COLOR (hps, f->background_color));
  2674.       WinEndPaint (hps);
  2675.  
  2676.       /* Send a redraw event to Emacs.  The minimal bounding rectangle
  2677.          for the update region is sent along with the event.  */
  2678.  
  2679.       pme.paint.header.type = PME_PAINT;
  2680.       pme.paint.header.frame = f->id;
  2681.       pme.paint.x0 = rclBound.xLeft / f->cxChar;
  2682.       pme.paint.x1 = rclBound.xRight / f->cxChar;
  2683.       pme.paint.y0 = (f->cyClient - rclBound.yTop) / f->cyChar;
  2684.       pme.paint.y1 = (f->cyClient - rclBound.yBottom) / f->cyChar;
  2685.       send_event (&pme);
  2686.       return (0);
  2687.  
  2688. #ifdef USE_PALETTE_MANAGER
  2689.     case WM_REALIZEPALETTE:
  2690.       if (has_palette_manager)
  2691.         {
  2692.           ULONG size;
  2693.  
  2694.           f = WinQueryWindowPtr (hwnd, 0);
  2695.           if (WinRealizePalette (hwnd, f->hpsClient, &size) && size != 0)
  2696.             WinInvalidateRect (hwnd, NULL, FALSE);
  2697.         }
  2698.       return ((MRESULT)FALSE);
  2699. #endif
  2700.  
  2701.     case WM_CLOSE:
  2702.  
  2703.       /* The window is to be closed on the user's request.  Send a
  2704.          delete-frame (or close-frame) event to Emacs and do not pass
  2705.          the message to the default window procedure (otherwise,
  2706.          WM_QUIT would be posted to the message queue).  */
  2707.  
  2708.       f = WinQueryWindowPtr (hwnd, 0);
  2709. #if 0
  2710.       pme.header.type = PME_WINDOWDELETE;
  2711.       pme.header.frame = f->id;
  2712.       send_event (&pme);
  2713. #else
  2714.       send_key (f, PMK_VIRTUAL, VK_CLOSE_FRAME, 0, 1);
  2715. #endif
  2716.       return (0);
  2717.  
  2718.     case WM_CHAR:
  2719.  
  2720.       /* Keyboard message.  Processing keys is quite messy as you will
  2721.          learn soon.  First, get the frame data pointer and extract
  2722.          the WM_CHAR message flags. */
  2723.  
  2724.       f = WinQueryWindowPtr (hwnd, 0);
  2725.       fsflags = (USHORT)SHORT1FROMMP (mp1);
  2726.  
  2727.       /* Ignore key-up messages.  Processing key-up messages might be
  2728.          useful for VK_NEWLINE and VK_ENTER (see below), but isn't
  2729.          implemented. */
  2730.  
  2731.       if (fsflags & KC_KEYUP)
  2732.         return (0);
  2733.  
  2734.       /* Extract the repeat count, the character code and the virtual
  2735.          key code. */
  2736.  
  2737.       usrep = CHAR3FROMMP (mp1);
  2738.       ussc = CHAR4FROMMP (mp1);
  2739.       usch = (USHORT)SHORT1FROMMP (mp2);
  2740.       usvk = (USHORT)SHORT2FROMMP (mp2);
  2741.  
  2742.       /* If it's a deadkey, remember the character code (it should
  2743.          have one) as it might be required if the user depresses a key
  2744.          which cannot be turned into a composite character.  Return
  2745.          after storing the character code. */
  2746.  
  2747.       if (fsflags & KC_DEADKEY)
  2748.         {
  2749.           f->deadkey = usch;
  2750.           return (0);
  2751.         }
  2752.  
  2753.       /* Handle invalid compositions: after depressing a deadkey, the
  2754.          user has depressed a key which cannot be turned into a
  2755.          composite character.  Insert the deadkey (accent) and
  2756.          continue processing with the second character.  We have to
  2757.          decrement the repeat count (that's not documented). */
  2758.  
  2759.       if (fsflags & KC_INVALIDCOMP)
  2760.         {
  2761.           if (f->deadkey == 0)
  2762.             return (0);
  2763.           send_key (f, PMK_ASCII, f->deadkey, 0, 1);
  2764.           if (usrep > 1)
  2765.             --usrep;
  2766.         }
  2767.       f->deadkey = 0;
  2768.  
  2769.       /* Ignore `ALT + keypad digit' keys -- they're used for entering
  2770.          the decimal code of a character and should not be seen by
  2771.          Emacs.  After releasing the ALT key, a WM_CHAR message for
  2772.          the character is sent where all the flag bits except KC_CHAR
  2773.          are zero.  Note that KC_VIRTUALKEY is not set for ALT-5,
  2774.          therefore we have to check the scan code to distinguish the
  2775.          keypad digit keys from the main keyboard digit keys.  This is
  2776.          dirty. */
  2777.  
  2778.       if ((fsflags & (KC_ALT|KC_SCANCODE|KC_CHAR)) == (KC_ALT|KC_SCANCODE))
  2779.         switch (ussc)
  2780.           {
  2781.           case 0x47: case 0x48: case 0x49:   /* 7 8 9 */
  2782.           case 0x4b: case 0x4c: case 0x4d:   /* 4 5 6 */
  2783.           case 0x4f: case 0x50: case 0x51:   /* 1 2 3 */
  2784.           case 0x52:                         /* 0     */
  2785.             return (0);
  2786.           }
  2787.  
  2788.       /* Handle some special virtual keys first. */
  2789.  
  2790.       if (fsflags & KC_VIRTUALKEY)
  2791.         switch (usvk)
  2792.           {
  2793.           case VK_ESC:
  2794.  
  2795.             /* The KC_CHAR bit is not set for the VK_ESC key.
  2796.                Therefore we have to handle that key here. */
  2797.  
  2798.             send_key (f, PMK_ASCII, 0x1b, fsflags, usrep);
  2799.             return (0);
  2800.  
  2801.           case VK_TAB:
  2802.  
  2803.             /* The KC_CHAR bit is not set for the VK_TAB key if code
  2804.                page 1004 is in effect.  Therefore we have to handle
  2805.                that key here. */
  2806.  
  2807.             if (fsflags & (KC_SHIFT|KC_CTRL|KC_ALT|KC_ALTGR))
  2808.               send_key (f, PMK_VIRTUAL, VK_TAB, fsflags, usrep);
  2809.             else
  2810.               send_key (f, PMK_ASCII, 0x09, fsflags, usrep);
  2811.             return (0);
  2812.  
  2813.           case VK_BACKSPACE:
  2814.  
  2815.             /* The KC_CHAR bit is set for the VK_BACKSPACE key.  As we
  2816.                want a `backspace' event instead of character 0x08, we
  2817.                handle VK_BACKSPACE here. */
  2818.  
  2819.             send_key (f, PMK_VIRTUAL, VK_BACKSPACE, fsflags, usrep);
  2820.             return (0);
  2821.  
  2822.           case VK_SPACE:
  2823.  
  2824.             /* Use character code 0x20 for space (with modifiers).  Do
  2825.                not do this if KC_CHAR is set, as space following a
  2826.                dead key should yield the accent. */
  2827.  
  2828.             if (!(fsflags & KC_CHAR))
  2829.               {
  2830.                 send_key (f, PMK_ASCII, 0x20, fsflags, usrep);
  2831.                 return (0);
  2832.               }
  2833.             break;
  2834.  
  2835.           case VK_NEWLINE:
  2836.  
  2837.             /* The NEWLINE and ENTER keys don't generate a code when
  2838.                depressed while ALT or SHIFT are depressed.  A code is
  2839.                generated when NEWLINE or ENTER are released!  This is
  2840.                probably a hack for people who keep their fingers too
  2841.                long on the shift keys, like me.  CTRL, however, works.
  2842.                For convenience (and compatibility), we translate C-RET
  2843.                to LFD here. */
  2844.  
  2845.             send_key (f, PMK_ASCII, (fsflags & KC_CTRL ? 0x0a : 0x0d),
  2846.                       0, usrep);
  2847.             return (0);
  2848.  
  2849.           case VK_ENTER:
  2850.  
  2851.             /* See above. */
  2852.  
  2853.             if (fsflags & KC_CTRL)
  2854.               send_key (f, PMK_ASCII, 0x0a, 0, usrep);
  2855.             else
  2856.               send_key (f, PMK_VIRTUAL, VK_ENTER, 0, usrep);
  2857.             return (0);
  2858.           }
  2859.  
  2860.       /* Treat keypad keys specially.  Check both the character code
  2861.          and the scan code to avoid problems with faked WM_CHAR
  2862.          events. */
  2863.  
  2864.       if ((fsflags & (KC_CHAR|KC_SCANCODE)) && ussc >= 0x37 && ussc <= 0x5c)
  2865.         for (i = 0; i < sizeof (keypad) / sizeof (keypad[0]); ++i)
  2866.           if (keypad[i].ch == usch && keypad[i].sc == ussc)
  2867.             {
  2868.               send_key (f, PMK_VIRTUAL, keypad[i].vk,
  2869.                         fsflags & ~keypad[i].clr, usrep);
  2870.               return (0);
  2871.             }
  2872.  
  2873.       /* If a character code is present, use that and throw away all
  2874.          the modifiers.  Letters with KC_ALT or KC_CTRL set don't have
  2875.          KC_CHAR set.  With a non-US keyboard, however, the
  2876.          Presentation Manager seems to ignore the CTRL key when the
  2877.          right ALT key (ALTGR) is depressed.  That is, KC_CHAR and
  2878.          KC_CTRL are set and the character code is not changed by the
  2879.          CTRL key.
  2880.  
  2881.          If the (left) ALT key is depressed together with the right
  2882.          ALT key (ALTGR), KC_CHAR is not set, that is, a user with a
  2883.          German keyboard cannot enter M-{ by typing ALT+ALTGR+7.  He
  2884.          or she has to type ESC { instead. */
  2885.  
  2886.       if (fsflags & KC_CHAR)
  2887.         {
  2888.           if ((fsflags & KC_CTRL) && (usch >= 0x40 && usch <= 0x5f))
  2889.             send_key (f, PMK_ASCII, usch & 0x1f, 0, usrep);
  2890.           else
  2891.             send_key (f, PMK_ASCII, usch, fsflags & KC_CTRL, usrep);
  2892.           return (0);
  2893.         }
  2894.  
  2895.       /* Now process keys depressed while the CTRL key is down.
  2896.          KC_CHAR and KC_VIRTUALKEY are not set for ASCII keys when the
  2897.          CTRL key is down.  If both the ALT and CTRL keys are down,
  2898.          process the key here.  Remove the KC_CTRL modifier and
  2899.          translate the character code if the character is a regular
  2900.          control character.  Otherwise, keep the character code and
  2901.          set the CTRL modifier.  Ignore keys for which the character
  2902.          code is zero.  This happens for the / key on the numeric
  2903.          keypad.  If the character is Ctrl-G, we send a signal instead
  2904.          of an event. */
  2905.  
  2906.       if ((fsflags & (KC_CTRL|KC_VIRTUALKEY)) == KC_CTRL)
  2907.         {
  2908.           if (usch >= 0x40 && usch <= 0x7f)
  2909.             {
  2910.               send_key (f, PMK_ASCII, usch & 0x1f, fsflags & KC_ALT, usrep);
  2911.               return (0);
  2912.             }
  2913.           else if ((usch & 0xff) != 0)
  2914.             {
  2915.               send_key (f, PMK_ASCII, usch, fsflags & (KC_CTRL|KC_ALT), usrep);
  2916.               return (0);
  2917.             }
  2918.         }
  2919.  
  2920.       /* Now process keys depressed while the ALT key is down (and the
  2921.          CTRL key is up).  Simply keep the character code and the ALT
  2922.          modifier.  Ignore keys for which the character code is zero.
  2923.          This happens for Alt+Altgr+@, for instance.  The reason is
  2924.          unknown. */
  2925.  
  2926.       if ((fsflags & (KC_ALT|KC_VIRTUALKEY)) == KC_ALT && (usch & 0xff) != 0)
  2927.         {
  2928.           send_key (f, PMK_ASCII, usch, KC_ALT, usrep);
  2929.           return (0);
  2930.         }
  2931.  
  2932.       /* If the key is a virtual key and hasn't been processed above,
  2933.          generate a system-defined keyboard event and keep all the
  2934.          modifiers (except SHIFT, in some cases).  Messages generated
  2935.          for shift keys etc. are ignored. */
  2936.  
  2937.       if (fsflags & KC_VIRTUALKEY)
  2938.         switch (usvk)
  2939.           {
  2940.           case VK_SHIFT:
  2941.           case VK_CTRL:
  2942.           case VK_ALT:
  2943.           case VK_ALTGRAF:
  2944.           case VK_NUMLOCK:
  2945.           case VK_CAPSLOCK:
  2946.  
  2947.             /* Ignore these keys. */
  2948.  
  2949.             return (0);
  2950.  
  2951.           case VK_BACKTAB:
  2952.  
  2953.             /* Remove the SHIFT modifier for the backtab key. */
  2954.  
  2955.             send_key (f, PMK_VIRTUAL, usvk, fsflags & ~KC_SHIFT, usrep);
  2956.             return (0);
  2957.  
  2958.           default:
  2959.  
  2960.             /* Remove the SHIFT modifier for the keys of the numeric
  2961.                keypad that share a number key (including . or ,) and a
  2962.                virtual key.  Otherwise, the HOME key, for instance,
  2963.                would yield `S-home' instead of `home' in numlock
  2964.                mode.  We look at the scan codes, which is dirty. */
  2965.  
  2966.             switch (ussc)
  2967.               {
  2968.               case 0x47: case 0x48: case 0x49:   /* 7 8 9 */
  2969.               case 0x4b: case 0x4c: case 0x4d:   /* 4 5 6 */
  2970.               case 0x4f: case 0x50: case 0x51:   /* 1 2 3 */
  2971.               case 0x52: case 0x53:              /* 0 .   */
  2972.                 fsflags &= ~KC_SHIFT;
  2973.                 break;
  2974.               }
  2975.  
  2976.             send_key (f, PMK_VIRTUAL, usvk, fsflags, usrep);
  2977.             return (0);
  2978.           }
  2979.  
  2980.       /* Generate keyboard events for keys that are neither character
  2981.          keys not virtual keys.  Both the KC_VIRTUALKEY and KC_CHAR
  2982.          bits are zero. We have to look at the scan code, which is
  2983.          dirty. */
  2984.  
  2985.       if (fsflags & KC_SCANCODE)
  2986.         switch (ussc)
  2987.           {
  2988.           case 0x4c:            /* `CENTER' key, `5' */
  2989.             send_key (f, PMK_VIRTUAL, VK_KP_CENTER, fsflags & ~KC_SHIFT, usrep);
  2990.             return (0);
  2991.  
  2992.           case 0x5c:            /* `DIVIDE' key on numeric keypad */
  2993.  
  2994.             /* This key doesn't generate a character code if the
  2995.                German keyboard layout is active.  Looks like a bug. */
  2996.  
  2997.             send_key (f, PMK_ASCII, '/', fsflags, usrep);
  2998.             return (0);
  2999.  
  3000.           default:
  3001.  
  3002.             /* When hitting a letter or digit key while the right Alt
  3003.                key (AltGr on most non-US keyboards) is down, neither
  3004.                KC_ALT nor KC_CHAR nor KC_VIRTUALKEY is set.  (On US
  3005.                keyboards, the right Alt key is equivalent to the left
  3006.                Alt key.)  Fortunately, the character code seems to be
  3007.                valid.  Use altgr_modifier and the character code if
  3008.                the character code looks valid.  Note that AltGr is
  3009.                used on most non-US keyboards to enter special
  3010.                characters; on the German keyboard, for instance, you
  3011.                have to type AltGr+q to get @.  These key combinations
  3012.                have been handled above (KC_CHAR is set). */
  3013.  
  3014.             if (usch > 0)
  3015.               send_key (f, PMK_ASCII, usch, fsflags | KC_ALTGR, usrep);
  3016.             break;
  3017.           }
  3018.       return (0);
  3019.  
  3020.     case WM_TRANSLATEACCEL:
  3021.  
  3022.       /* Examine accelerator table.  We ignore the accelerator table
  3023.          for keys which are not listed in the shortcuts frame
  3024.          parameter.  By ignoring the accelerator table, we receive
  3025.          WM_CHAR messages for F1, F10, A-f4, A-space etc. */
  3026.  
  3027.       f = WinQueryWindowPtr (hwnd, 0);
  3028.  
  3029.       /* If all shortcuts are disabled, don't check the character.  */
  3030.  
  3031.       if (f->shortcuts == 0)
  3032.         return (FALSE);
  3033.  
  3034.       /* Get the parameters of the WM_CHAR message.  */
  3035.  
  3036.       pqmsg = PVOIDFROMMP (mp1);
  3037.       fsflags = (USHORT)SHORT1FROMMP (pqmsg->mp1);
  3038.       usch = (USHORT)SHORT1FROMMP (pqmsg->mp2);
  3039.       usvk = (USHORT)SHORT2FROMMP (pqmsg->mp2);
  3040.  
  3041.       /* Compute the SHORTCUT_* bit. */
  3042.  
  3043.       bit = 0;
  3044.       if (fsflags & KC_VIRTUALKEY)
  3045.         switch (usvk)
  3046.           {
  3047.           case VK_ALT:
  3048.             bit = SHORTCUT_ALT;
  3049.             break;
  3050.           case VK_ALTGRAF:
  3051.             bit = SHORTCUT_ALTGR;
  3052.             break;
  3053.           case VK_F1:
  3054.             bit = SHORTCUT_F1;
  3055.             break;
  3056.           case VK_F4:
  3057.             if (fsflags & KC_ALT) bit = SHORTCUT_ALT_F4;
  3058.             break;
  3059.           case VK_F5:
  3060.             if (fsflags & KC_ALT) bit = SHORTCUT_ALT_F5;
  3061.             break;
  3062.           case VK_F6:
  3063.             if (fsflags & KC_ALT) bit = SHORTCUT_ALT_F6;
  3064.             break;
  3065.           case VK_F7:
  3066.             if (fsflags & KC_ALT) bit = SHORTCUT_ALT_F7;
  3067.             break;
  3068.           case VK_F8:
  3069.             if (fsflags & KC_ALT) bit = SHORTCUT_ALT_F8;
  3070.             break;
  3071.           case VK_F9:
  3072.             if (fsflags & KC_ALT) bit = SHORTCUT_ALT_F9;
  3073.             break;
  3074.           case VK_F10:
  3075.             bit = (fsflags & KC_ALT) ? SHORTCUT_ALT_F10 : SHORTCUT_F1;
  3076.             break;
  3077.           case VK_F11:
  3078.             if (fsflags & KC_ALT) bit = SHORTCUT_ALT_F11;
  3079.             break;
  3080.           }
  3081.       else if (fsflags & KC_CHAR)
  3082.         switch (usch)
  3083.           {
  3084.           case ' ':
  3085.             if (fsflags & KC_ALT) bit = SHORTCUT_ALT_SPACE;
  3086.             break;
  3087.           }
  3088.  
  3089.       /* If the key is known and not enabled in f->shortcuts, ignore
  3090.          the accelerator table. */
  3091.  
  3092.       if (bit != 0 && !(f->shortcuts & bit))
  3093.         return (FALSE);
  3094.       break;
  3095.  
  3096.     case WM_COMMAND:
  3097.  
  3098.       /* Menu or button or accelerator. */
  3099.  
  3100.       f = WinQueryWindowPtr (hwnd, 0);
  3101.       cmd = COMMANDMSG (&msg)->cmd;
  3102.       if (COMMANDMSG (&msg)->source == CMDSRC_MENU
  3103.           && cmd >= IDM_MENU && cmd <= IDM_MENU_LAST)
  3104.         {
  3105.           send_answer (menu_serial, NULL, 0, cmd - IDM_MENU);
  3106.           f->popup_active = FALSE;
  3107.           return (0);
  3108.         }
  3109.       else if (COMMANDMSG (&msg)->source == CMDSRC_MENU
  3110.                && cmd >= IDM_MENUBAR && cmd <= IDM_MENUBAR_LAST)
  3111.         {
  3112.           /* An item of a submenu of the menubar. */
  3113.  
  3114.           pme.menubar.header.type = PME_MENUBAR;
  3115.           pme.menubar.header.frame = f->id;
  3116.           pme.menubar.number = f->menubar_id_map[cmd - IDM_MENUBAR];
  3117.           send_event (&pme);
  3118.           return (0);
  3119.         }
  3120.       break;
  3121.  
  3122.     case WM_MENUEND:
  3123.  
  3124.       /* Menu terminates.  If menu_selected is FALSE, the user hasn't
  3125.          selected a menu item of a popup menu.  That is, the menu was
  3126.          dismissed by pressing ESC or clicking outside the menu.  We
  3127.          send a 0 to Emacs by faking a menu selection. */
  3128.  
  3129.       f = WinQueryWindowPtr (hwnd, 0);
  3130.       if ((HWND)LONGFROMMP (mp2) == f->hwndPopupMenu)
  3131.         {
  3132.           if (!menu_selected)
  3133.             {
  3134.               send_answer (menu_serial, NULL, 0, 0);
  3135.               f->popup_active = FALSE;
  3136.             }
  3137.         }
  3138.       return (0);
  3139.  
  3140.     case WM_MENUSELECT:
  3141.  
  3142.       /* A menu item has been selected.  Set menu_selected if
  3143.          appropriate. */
  3144.  
  3145.       if (SHORT2FROMMP (mp1))
  3146.         {
  3147.           f = WinQueryWindowPtr (hwnd, 0);
  3148.           cmd = SHORT1FROMMP (mp1);
  3149.           if (cmd >= IDM_MENU && cmd <= IDM_MENU_LAST && f->popup_active)
  3150.             menu_selected = TRUE;
  3151.         }
  3152.       break;
  3153.  
  3154.     case WM_BUTTON1DOWN:
  3155.       if (mouse_button (hwnd, mp1, mp2, 0, down_modifier))
  3156.         return ((MRESULT)TRUE);
  3157.       break;
  3158.  
  3159.     case WM_BUTTON1UP:
  3160.       if (mouse_button (hwnd, mp1, mp2, 0, up_modifier))
  3161.         return ((MRESULT)TRUE);
  3162.       break;
  3163.  
  3164.     case WM_BUTTON1DBLCLK:
  3165.       if (mouse_button (hwnd, mp1, mp2, 0, down_modifier))
  3166.         return ((MRESULT)TRUE);
  3167.       break;
  3168.  
  3169.     case WM_BUTTON2DOWN:
  3170.       if (mouse_button (hwnd, mp1, mp2, 1, down_modifier))
  3171.         return ((MRESULT)TRUE);
  3172.       break;
  3173.  
  3174.     case WM_BUTTON2UP:
  3175.       if (mouse_button (hwnd, mp1, mp2, 1, up_modifier))
  3176.         return ((MRESULT)TRUE);
  3177.       break;
  3178.  
  3179.     case WM_BUTTON2DBLCLK:
  3180.       if (mouse_button (hwnd, mp1, mp2, 1, down_modifier))
  3181.         return ((MRESULT)TRUE);
  3182.       break;
  3183.  
  3184.     case WM_BUTTON3DOWN:
  3185.       if (mouse_button (hwnd, mp1, mp2, 2, down_modifier))
  3186.         return ((MRESULT)TRUE);
  3187.       break;
  3188.  
  3189.     case WM_BUTTON3UP:
  3190.       if (mouse_button (hwnd, mp1, mp2, 2, up_modifier))
  3191.         return ((MRESULT)TRUE);
  3192.       break;
  3193.  
  3194.     case WM_BUTTON3DBLCLK:
  3195.       if (mouse_button (hwnd, mp1, mp2, 2, down_modifier))
  3196.         return ((MRESULT)TRUE);
  3197.       break;
  3198.  
  3199.     case WM_SIZE:
  3200.  
  3201.       /* The size of the window has changed.  Store the new size and
  3202.          recreate the cursor.  The cursor must be recreated to install
  3203.          the new clipping rectangle.  While minimizing or restoring
  3204.          the window, this message is ignored. */
  3205.  
  3206.       f = WinQueryWindowPtr (hwnd, 0);
  3207.       if (f->ignore_wm_size || f->stage < STAGE_READY)
  3208.         break;
  3209.       if (f->minimizing)
  3210.         {
  3211.           f->minimizing = FALSE;
  3212.           break;
  3213.         }
  3214.       if (f->restoring)
  3215.         {
  3216.           f->restoring = FALSE;
  3217.           set_size (f);
  3218.           break;
  3219.         }
  3220.       f->cxClient = SHORT1FROMMP (mp2);
  3221.       f->cyClient = SHORT2FROMMP (mp2);
  3222.       set_cursor (f);
  3223.       /* Notify Emacs of the new window size. */
  3224.       report_size (f);
  3225.       break;
  3226.  
  3227.     case WM_MOVE:
  3228.  
  3229.       /* The position of the window has been changed. */
  3230.  
  3231.       f = WinQueryWindowPtr (hwnd, 0);
  3232.       WinQueryWindowPos (f->hwndClient, &swp);
  3233.       pme.framemove.header.type = PME_FRAMEMOVE;
  3234.       pme.framemove.header.frame = f->id;
  3235.       ptl.x = swp.x; ptl.y = swp.y;
  3236.       WinMapWindowPoints (f->hwndFrame, HWND_DESKTOP, &ptl, 1);
  3237.       pme.framemove.top = swpScreen.cy - 1 - (ptl.y + swp.cy);
  3238.       pme.framemove.left = ptl.x;
  3239.       send_event (&pme);
  3240.       break;
  3241.  
  3242.     case WM_MOUSEMOVE:
  3243.       if (track_mouse)
  3244.         {
  3245.           f = WinQueryWindowPtr (hwnd, 0);
  3246.           pme.mouse.x = charbox_x (f, (USHORT)SHORT1FROMMP (mp1));
  3247.           pme.mouse.y = charbox_y (f, (USHORT)SHORT2FROMMP (mp1));
  3248.           if (pme.mouse.x >= 0 && pme.mouse.y >= 0
  3249.               && (pme.mouse.x != track_x || pme.mouse.y != track_y))
  3250.             {
  3251.               pme.mouse.header.type = PME_MOUSEMOVE;
  3252.               pme.mouse.header.frame = f->id;
  3253.               send_event (&pme);
  3254.               track_x = pme.mouse.x; track_y = pme.mouse.y;
  3255.             }
  3256.         }
  3257.       break;
  3258.  
  3259.     case WM_SETFOCUS:
  3260.  
  3261.       /* Create the cursor when receiving the input focus, destroy the
  3262.          cursor when loosing the input focus. */
  3263.  
  3264.       f = WinQueryWindowPtr (hwnd, 0);
  3265.       if (SHORT1FROMMP (mp2))
  3266.         create_cursor (f);
  3267.       else
  3268.         WinDestroyCursor (hwnd);
  3269.       return (0);
  3270.  
  3271.     case WM_SHOW:
  3272.  
  3273.       /* Call initial_show_frame() if a frame is initially made
  3274.          visible via the Window List.  Also notify Emacs. */
  3275.  
  3276.       if (SHORT1FROMMP (mp1))
  3277.         {
  3278.           f = WinQueryWindowPtr (hwnd, 0);
  3279.           if (f->stage == STAGE_INVISIBLE)
  3280.             {
  3281.               /* Don't call initial_show_frame() from within WM_SHOW
  3282.                  processing. */
  3283.               WinPostMsg (f->hwndClient, UWM_INITIAL_SHOW, MPFROMP (f), 0);
  3284.               /* Notify Emacs. */
  3285.               pme.header.type = PME_RESTORE;
  3286.               pme.header.frame = f->id;
  3287.               send_event (&pme);
  3288.             }
  3289.         }
  3290.       return (0);
  3291.  
  3292.     case WM_VSCROLL:
  3293.       handle_vscroll (hwnd, mp1, mp2);
  3294.       return (0);
  3295.  
  3296.     case WM_PRESPARAMCHANGED:
  3297.       if (LONGFROMMP (mp1) == PP_BACKGROUNDCOLOR)
  3298.         drop_color (hwnd);
  3299.       return 0;
  3300.  
  3301.     case DM_DRAGOVER:
  3302.  
  3303.       /* Determine whether the object can be dropped. */
  3304.  
  3305.       return (drag_over ((PDRAGINFO)mp1));
  3306.  
  3307.     case DM_DROP:
  3308.  
  3309.       /* Drop an object. */
  3310.  
  3311.       return (drag_drop (hwnd, (PDRAGINFO)mp1));
  3312.  
  3313.     case UWM_POPUPMENU:
  3314.       f = (frame_data *)mp1;
  3315.       md = (menu_data *)mp2;
  3316.       if (f->hwndPopupMenu != NULLHANDLE)
  3317.         WinDestroyWindow (f->hwndPopupMenu);
  3318.       f->hwndPopupMenu = create_popup_menu (f, md->data, md->str);
  3319.       options = (PU_MOUSEBUTTON1 | PU_MOUSEBUTTON2 | PU_MOUSEBUTTON3
  3320.                  | PU_KEYBOARD | PU_HCONSTRAIN | PU_VCONSTRAIN);
  3321.       if (md->align_top)
  3322.         options |= PU_POSITIONONITEM;
  3323.       if (md->button >= 1 && md->button <= 3)
  3324.         for (i = 0; i < 3; ++i)
  3325.           if (f->buttons[i] == md->button)
  3326.             switch (i)
  3327.               {
  3328.               case 0:
  3329.                 if (WinGetKeyState (HWND_DESKTOP, VK_BUTTON1) & 0x8000)
  3330.                   options |= PU_MOUSEBUTTON1DOWN;
  3331.                 break;
  3332.               case 1:
  3333.                 if (WinGetKeyState (HWND_DESKTOP, VK_BUTTON2) & 0x8000)
  3334.                   options |= PU_MOUSEBUTTON2DOWN;
  3335.                 break;
  3336.               case 2:
  3337.                 if (WinGetKeyState (HWND_DESKTOP, VK_BUTTON3) & 0x8000)
  3338.                   options |= PU_MOUSEBUTTON3DOWN;
  3339.                 break;
  3340.               }
  3341.       menu_selected = FALSE;
  3342.       capture (f, FALSE);
  3343.       if (!WinPopupMenu (f->hwndFrame, hwnd, f->hwndPopupMenu,
  3344.                          md->x, md->y, first_id, options))
  3345.         send_answer (menu_serial, NULL, 0, 0);
  3346.       return (0);
  3347.  
  3348.     case UWM_DIALOG:
  3349.       f = (frame_data *)mp1;
  3350.       md = (menu_data *)mp2;
  3351.       /* WinCreateDlg must not be called while the mouse is captured. */
  3352.       capture (f, FALSE);
  3353.       hwndDlg = create_dialog (f, md->button, md->data, md->str);
  3354.       DosPostEventSem (hevDone);
  3355.       dlg_answer = 0;
  3356.       if (hwndDlg != NULLHANDLE)
  3357.         dlg_answer = WinProcessDlg (hwndDlg);
  3358.       send_answer (dialog_serial, NULL, 0, dlg_answer);
  3359.       return (0);
  3360.  
  3361.     case UWM_FILEDIALOG:
  3362.       /* WinCreateDlg must not be called while the mouse is captured.
  3363.          Perhaps that's also true for WinFileDlg.  */
  3364.  
  3365.       f = WinQueryWindowPtr (hwnd, 0);
  3366.       capture (f, FALSE);
  3367.  
  3368.       file_dialog ((frame_data *)mp1, (pm_filedialog *)mp2);
  3369.       return (0);
  3370.  
  3371.     case UWM_MENUBAR:
  3372.       f = (frame_data *)mp1;
  3373.       mbd = (menubar_data *)mp2;
  3374.       create_menubar (f, mbd->data, mbd->str, mbd->el_count, mbd->str_bytes);
  3375.       if (mbd->data != NULL)
  3376.         DosPostEventSem (hevDone);
  3377.       return (0);
  3378.  
  3379.     case UWM_CREATE_SCROLLBAR:
  3380.       f = (frame_data *)mp1;
  3381.       create_scrollbar (f, (pm_create_scrollbar *)mp2);
  3382.       DosPostEventSem (hevDone);
  3383.       return (0);
  3384.  
  3385.     case UWM_DESTROY_SCROLLBAR:
  3386.       destroy_scrollbar (SHORT1FROMMP (mp2));
  3387.       return (0);
  3388.  
  3389.     case UWM_INITIAL_SHOW:
  3390.       f = (frame_data *)mp1;
  3391.       initial_show_frame (f);
  3392.       return (0);
  3393.     }
  3394.   return (WinDefWindowProc (hwnd, msg, mp1, mp2));
  3395. }
  3396.  
  3397.  
  3398. /* This window procedure is used for subclassing frame windows.  We
  3399.    subclass frame windows to set the grid for resizing the window to
  3400.    the character box.  */
  3401.  
  3402. static MRESULT FrameWndProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  3403. {
  3404.   HWND hwndClient;
  3405.   frame_data *f;
  3406.   MRESULT mr;
  3407.   PTRACKINFO pti;
  3408.   PSWP pswp;
  3409.   pm_event pme;
  3410.  
  3411.   switch (msg)
  3412.     {
  3413.     case WM_QUERYTRACKINFO:
  3414.  
  3415.       /* Fill-in TRACKINFO structure for resizing or moving a window.
  3416.          First call the normal frame window procedure to set up the
  3417.          structure. */
  3418.  
  3419.       mr = old_frame_proc (hwnd, msg, mp1, mp2);
  3420.       pti = (PTRACKINFO)mp2;
  3421.  
  3422.       /* If we're resizing the window, set the grid. */
  3423.  
  3424.       if (((pti->fs & TF_MOVE) != TF_MOVE)
  3425.           && ((pti->fs & TF_MOVE) || (pti->fs & TF_SETPOINTERPOS)))
  3426.         {
  3427.           hwndClient = WinWindowFromID (hwnd, FID_CLIENT);
  3428.           f = WinQueryWindowPtr (hwndClient, 0);
  3429.           if (f->cxChar != 0 && f->cyChar != 0)
  3430.             {
  3431.               pti->fs |= TF_GRID;
  3432.               pti->cxGrid = pti->cxKeyboard = f->cxChar;
  3433.               pti->cyGrid = pti->cyKeyboard = f->cyChar;
  3434.             }
  3435.         }
  3436.       return (mr);
  3437.  
  3438.     case WM_MINMAXFRAME:
  3439.  
  3440.       /* The frame window is being minimized or maximized.  We set a
  3441.          flag to suppress sending a resize event to Emacs when
  3442.          minimizing the window. */
  3443.  
  3444.       hwndClient = WinWindowFromID (hwnd, FID_CLIENT);
  3445.       f = WinQueryWindowPtr (hwndClient, 0);
  3446.       pswp = (PSWP)mp1;
  3447.       if (!f->minimized && (pswp->fl & SWP_MINIMIZE))
  3448.         {
  3449.           f->minimizing = f->minimized = TRUE;
  3450.           pme.header.type = PME_MINIMIZE;
  3451.           pme.header.frame = f->id;
  3452.           send_event (&pme);
  3453.         }
  3454.       else if (f->minimized && (pswp->fl & (SWP_RESTORE | SWP_MAXIMIZE)))
  3455.         {
  3456.           f->minimized = FALSE;
  3457.           if (pswp->fl & SWP_RESTORE)
  3458.             f->restoring = TRUE;
  3459.           pme.header.type = PME_RESTORE;
  3460.           pme.header.frame = f->id;
  3461.           send_event (&pme);
  3462.         }
  3463.       break;
  3464.     }
  3465.   return (old_frame_proc (hwnd, msg, mp1, mp2));
  3466. }
  3467.  
  3468.  
  3469. /* This is the window procedure for the object window of the PM
  3470.    thread.  It is used for creating and destroying windows as windows
  3471.    belong to the thread by which they have been created.  If there
  3472.    were no object window, we had no window of the PM thread to which
  3473.    the messages could be sent. */
  3474.  
  3475. static MRESULT ObjectWndProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  3476. {
  3477.   ULONG flFrameFlags;
  3478.   frame_data *f;
  3479.   PFNWP fun;
  3480.  
  3481.   switch (msg)
  3482.     {
  3483.     case UWM_CREATE:
  3484.       f = (frame_data *)mp1;
  3485.  
  3486.       /* Dirty trick: WinCreateStdWindow() doesn't accept a pCtlData
  3487.          parameter.  As using WinCreateWindow() would be too complicated
  3488.          for a frame window, the pointer is passed in a global variable.
  3489.          As this code cannot be reentered, this is safe. */
  3490.  
  3491.       new_frame = f;
  3492.       flFrameFlags = (FCF_TITLEBAR      | FCF_SYSMENU | FCF_ICON |
  3493.                       FCF_SIZEBORDER    | FCF_MINMAX  |
  3494.                       FCF_SHELLPOSITION | FCF_TASKLIST);
  3495.       f->hwndFrame = WinCreateStdWindow (HWND_DESKTOP, 0,
  3496.                                          &flFrameFlags, szClientClass,
  3497.                                          NULL, 0L, 0, 1, NULL);
  3498.       fun = WinSubclassWindow (f->hwndFrame, FrameWndProc);
  3499.       if (old_frame_proc != NULL && old_frame_proc != fun)
  3500.         abort ();
  3501.       old_frame_proc = fun;
  3502.       return (0);
  3503.  
  3504.     case UWM_DESTROY:
  3505.       f = (frame_data *)mp1;
  3506.       WinDestroyWindow (f->hwndFrame);
  3507.       return (0);
  3508.     }
  3509.   return (0);
  3510. }
  3511.  
  3512.  
  3513. /* Receive SIZE bytes from Emacs proper.  The data is stored to
  3514.    DST. */
  3515.  
  3516. static void receive (void *dst, ULONG size)
  3517. {
  3518.   char *d;
  3519.   ULONG n;
  3520.  
  3521.   d = dst;
  3522.   while (size != 0)
  3523.     {
  3524.       if (DosRead (inbound_pipe, d, size, &n) != 0 || n == 0)
  3525.         pm_error ("Cannot read from pipe");
  3526.       size -= n;
  3527.       d += n;
  3528.     }
  3529. }
  3530.  
  3531.  
  3532. /* Return a pointer to the frame data for the frame with identity FID.
  3533.    If no such frame exists, NULL is returned, causing a protection
  3534.    violation later (this must not happen). */
  3535.  
  3536. static frame_data *find_frame (ULONG fid)
  3537. {
  3538.   frame_data *f;
  3539.  
  3540.   for (f = hash_table[fid % HASH_SIZE]; f != NULL; f = f->next)
  3541.     if (f->id == fid)
  3542.       return (f);
  3543.   return (NULL);
  3544. }
  3545.  
  3546.  
  3547. /* Create a new frame.  This function is called in the thread which
  3548.    reads the pipe.  Therefore, we must send a message to the PM thread
  3549.    to actually create the PM window. */
  3550.  
  3551. static void create_frame (pmr_create *pc)
  3552. {
  3553.   frame_data *f;
  3554.   ULONG fid, hash;
  3555.  
  3556.   fid = pc->header.frame;
  3557.   hash = fid % HASH_SIZE;
  3558.   f = allocate (sizeof (*f));
  3559.   f->next = hash_table[hash];
  3560.   hash_table[hash] = f;
  3561.   f->id = fid;
  3562.   f->width = pc->width;
  3563.   f->height = pc->height;
  3564.   f->sb_width = 0;
  3565.   f->background_color = RGB_WHITE;
  3566.   f->cursor_x = 0; f->cursor_y = 0;
  3567.   f->cursor_on = FALSE;
  3568.   f->cursor_type = CURSORTYPE_BOX;
  3569.   f->cursor_width = 0;
  3570.   f->cursor_blink = TRUE;
  3571.   f->ignore_button_up = 0;
  3572.   f->stage = STAGE_CREATING;
  3573.   f->initial_visibility = 0;
  3574.   f->initial_left_base = 0;
  3575.   f->initial_top_base = 0;
  3576.   f->minimized = FALSE;
  3577.   f->minimizing = FALSE;
  3578.   f->restoring = FALSE;
  3579.   f->ignore_wm_size = FALSE;
  3580.   f->deadkey = 0;
  3581.   f->shortcuts = 0;
  3582.   f->popup_active = FALSE;
  3583.   f->menubar_id_map = NULL;
  3584.   f->menubar_id_allocated = 0;
  3585.   f->menubar_id_used = 0;
  3586.   f->cur_menubar_el = NULL;
  3587.   f->cur_menubar_el_allocated = 0;
  3588.   f->cur_menubar_str = NULL;
  3589.   f->cur_menubar_str_allocated = 0;
  3590.   f->initial_menubar_el_count = 0;
  3591.   f->initial_menubar_str_bytes = 0;
  3592.   f->buttons[0] = 1;
  3593.   f->buttons[1] = 3;
  3594.   f->buttons[2] = 2;
  3595.   f->menu_font[0] = 0;          /* Use the default font */
  3596.   memset (f->font_defined, 0, sizeof (f->font_defined));
  3597.  
  3598.   /* We must not create the cursor until cxChar and cyChar are known.
  3599.      Therefore we set both to zero before creating the window to be
  3600.      able to detect that case. */
  3601.  
  3602.   f->cxChar = 0; f->cyChar = 0;
  3603.  
  3604.   f->var_width_fonts = FALSE;
  3605.   parse_font_spec (&f->font, DEFAULT_FONT);
  3606.   f->alt_modifier = meta_modifier;
  3607.   f->altgr_modifier = alt_modifier;
  3608.   WinSendMsg (hwndObject, UWM_CREATE, MPFROMP (f), 0);
  3609. }
  3610.  
  3611.  
  3612. /* Destroy a frame.  A window can be destroyed only by the thread
  3613.    which created it.  Therefore we send a message to the PM thread to
  3614.    actually destroy the window. */
  3615.  
  3616. static void destroy_frame (ULONG fid)
  3617. {
  3618.   frame_data *f, **fp;
  3619.  
  3620.   for (fp = &hash_table[fid % HASH_SIZE]; *fp != NULL; fp = &(*fp)->next)
  3621.     {
  3622.       f = *fp;
  3623.       if (f->id == fid)
  3624.         {
  3625.           *fp = f->next;
  3626.           WinSendMsg (hwndObject, UWM_DESTROY, MPFROMP (f), 0);
  3627.           deallocate (f->menubar_id_map);
  3628.           deallocate (f->cur_menubar_el);
  3629.           deallocate (f->cur_menubar_str);
  3630.           deallocate (f);
  3631.           return;
  3632.         }
  3633.     }
  3634. }
  3635.  
  3636.  
  3637. static void set_pos (frame_data *f, int left, int left_base,
  3638.                      int top, int top_base)
  3639. {
  3640.   SWP swp;
  3641.   RECTL rcl;
  3642.   POINTL ptl;
  3643.  
  3644.   if (f->stage < STAGE_READY)
  3645.     {
  3646.       if (left_base != 0)
  3647.         {
  3648.           f->initial_left = left; f->initial_left_base = left_base;
  3649.         }
  3650.       if (top_base != 0)
  3651.         {
  3652.           f->initial_top = top; f->initial_top_base = top_base;
  3653.         }
  3654.       return;
  3655.     }
  3656.   WinQueryWindowPos (f->hwndClient, &swp);
  3657.   ptl.x = swp.x;                /* Lower left-hand corner */
  3658.   ptl.y = swp.y;
  3659.   WinMapWindowPoints (f->hwndFrame, HWND_DESKTOP, &ptl, 1);
  3660.   swp.x = ptl.x;
  3661.   swp.y = ptl.y;
  3662.   if (left_base > 0)
  3663.     swp.x = left;
  3664.   else if (left_base < 0)
  3665.     swp.x = swpScreen.cx - swp.cx + left;
  3666.   if (top_base > 0)
  3667.     swp.y = swpScreen.cy - 1 - (top + swp.cy);
  3668.   else if (top_base < 0)
  3669.     swp.y = -top;
  3670.   rcl.xLeft = swp.x; rcl.xRight = swp.x + swp.cx;
  3671.   rcl.yBottom = swp.y; rcl.yTop = swp.y + swp.cy;
  3672.   WinCalcFrameRect (f->hwndFrame, &rcl, FALSE);
  3673.   swp.x = rcl.xLeft;
  3674.   swp.y = rcl.yBottom;
  3675.   WinSetWindowPos (f->hwndFrame, HWND_TOP, swp.x, swp.y, 0, 0, SWP_MOVE);
  3676. }
  3677.  
  3678.  
  3679. #define CONV_BUTTON(c) ((c) >= '1' && (c) <= '3' ? (c) - '0' : 0)
  3680.  
  3681. /* Modify parameters of an existing frame. */
  3682.  
  3683. static void modify_frame (frame_data *f)
  3684. {
  3685.   pm_modify more;
  3686.   int repaint;
  3687.  
  3688.   receive (&more, sizeof (more));
  3689.   repaint = FALSE;
  3690.   if (more.var_width_fonts != 0)
  3691.     f->var_width_fonts = (more.var_width_fonts == PMR_TRUE);
  3692.   if (more.background_color != COLOR_NONE)
  3693.     {
  3694.       f->background_color = more.background_color;
  3695.       repaint = TRUE;
  3696.     }
  3697.   if (more.default_font[0] != 0)
  3698.     {
  3699.       parse_font_spec (&f->font, more.default_font);
  3700.       set_default_font (f);
  3701.       set_cursor (f);
  3702.       if (more.width == 0 && more.height == 0)
  3703.         set_size (f);
  3704.     }
  3705.   if (more.menu_font_set)
  3706.     {
  3707.       strcpy (f->menu_font, more.menu_font);
  3708.       update_menu_font (f);
  3709.     }
  3710.   if (more.width != 0 || more.height != 0 || more.sb_width != -1)
  3711.     {
  3712.       if (more.width != 0) f->width = more.width;
  3713.       if (more.height != 0) f->height = more.height;
  3714.       if (more.sb_width != -1) f->sb_width = more.sb_width;
  3715.       set_size (f);
  3716.     }
  3717.   if (more.left_base != 0 || more.top_base != 0)
  3718.     set_pos (f, more.left, more.left_base, more.top, more.top_base);
  3719.   if (more.cursor_type != 0)
  3720.     {
  3721.       f->cursor_type = more.cursor_type;
  3722.       f->cursor_width = more.cursor_width;
  3723.     }
  3724.   if (more.cursor_blink != 0)
  3725.     f->cursor_blink = (more.cursor_blink == PMR_TRUE);
  3726.   if (more.cursor_type != 0 || more.cursor_blink != 0)
  3727.     set_cursor (f);
  3728.   if (more.alt_modifier != 0)
  3729.     f->alt_modifier = more.alt_modifier;
  3730.   if (more.altgr_modifier != 0)
  3731.     f->altgr_modifier = more.altgr_modifier;
  3732.   if (more.shortcuts != 0)
  3733.     f->shortcuts = more.shortcuts & ~SHORTCUT_SET;
  3734.   if (more.buttons[0] != 0)
  3735.     {
  3736.       /* Note that the middle button is button 3 (not 2) under OS/2. */
  3737.       f->buttons[0] = CONV_BUTTON (more.buttons[0]);
  3738.       f->buttons[1] = CONV_BUTTON (more.buttons[2]);
  3739.       f->buttons[2] = CONV_BUTTON (more.buttons[1]);
  3740.     }
  3741.   if (repaint)
  3742.     WinInvalidateRect (f->hwndClient, NULL, FALSE);
  3743. }
  3744.  
  3745.  
  3746. /* Get the current mouse position and send an appropriate answer to
  3747.    Emacs. */
  3748.  
  3749. static void get_mousepos (int serial)
  3750. {
  3751.   HWND hwnd;
  3752.   POINTL ptl;
  3753.   frame_data *f;
  3754.   int i;
  3755.   pmd_mousepos result;
  3756.  
  3757.   result.frame = 0; result.x = 0; result.y = 0;
  3758.   WinQueryPointerPos (HWND_DESKTOP, &ptl);
  3759.   hwnd = WinWindowFromPoint (HWND_DESKTOP, &ptl, TRUE);
  3760.   if (hwnd != NULLHANDLE)
  3761.     {
  3762.       for (i = 0; i < HASH_SIZE; ++i)
  3763.         for (f = hash_table[i]; f != NULL; f = f->next)
  3764.           if (hwnd == f->hwndClient)
  3765.             {
  3766.               WinMapWindowPoints (HWND_DESKTOP, f->hwndClient, &ptl, 1);
  3767.               result.frame = f->id;
  3768.               result.x = charbox_x (f, ptl.x);
  3769.               result.y = charbox_y (f, ptl.y);
  3770.               if (result.x < 0) result.x = 0;
  3771.               if (result.y < 0) result.y = 0;
  3772.               break;
  3773.             }
  3774.     }
  3775.   send_answer (serial, &result, sizeof (result), 0);
  3776. }
  3777.  
  3778.  
  3779. /* Get the position of the frame window and send an appropriate answer
  3780.    to Emacs. */
  3781.  
  3782. static void get_framepos (frame_data *f, int serial)
  3783. {
  3784.   SWP swp;
  3785.   POINTL ptl;
  3786.   pmd_framepos result;
  3787.  
  3788.   WinQueryWindowPos (f->hwndClient, &swp);
  3789.   ptl.x = swp.x; ptl.y = swp.y;
  3790.   WinMapWindowPoints (f->hwndFrame, HWND_DESKTOP, &ptl, 1);
  3791.   result.left = ptl.x;
  3792.   result.top = swpScreen.cy - 1 - (ptl.y + swp.cy);
  3793.   result.pix_width = f->width;  /* TODO */
  3794.   result.pix_height = f->height; /* TODO */
  3795.   send_answer (serial, &result, sizeof (result), 0);
  3796. }
  3797.  
  3798.  
  3799. /* Get the width or height of the screen in pixels and in
  3800.    millimeters. */
  3801.  
  3802. static void get_dimen (HDC hdc, ULONG caps_dimen, ULONG caps_res,
  3803.                        int *ppix, int *pmm)
  3804. {
  3805.   LONG dimen, res;
  3806.  
  3807.   /* Retrieve the width or height in pixels. */
  3808.  
  3809.   if (!DevQueryCaps (hdc, caps_dimen, 1, &dimen))
  3810.     {
  3811.       *ppix = 1; *pmm = 1;
  3812.     }
  3813.   else
  3814.     {
  3815.       *ppix = dimen;
  3816.  
  3817.       /* Retrieve and the resolution in pixels per meter. */
  3818.  
  3819.       if (!DevQueryCaps (hdc, caps_res, 1, &res)
  3820.           || res == 0)
  3821.         *pmm = 1;
  3822.       else
  3823.         *pmm = (1000 * dimen) / res;
  3824.     }
  3825. }
  3826.  
  3827.  
  3828. /* Get the number of planes etc. and send an appropriate answer to
  3829.    Emacs. */
  3830.  
  3831. static void get_config (int serial)
  3832. {
  3833.   HPS hps;
  3834.   HDC hdc;
  3835.   LONG colors;
  3836.   pmd_config result;
  3837.  
  3838.   hps = WinGetScreenPS (HWND_DESKTOP);
  3839.   hdc = GpiQueryDevice (hps);
  3840.   DevQueryCaps (hdc, CAPS_PHYS_COLORS, 1, &colors);
  3841.   result.planes = ffs (colors) - 1;
  3842.   DevQueryCaps (hdc, CAPS_COLORS, 1, &colors);
  3843.   result.color_cells = (int)colors;
  3844.   get_dimen (hdc, CAPS_WIDTH, CAPS_HORIZONTAL_RESOLUTION,
  3845.              &result.width, &result.width_mm);
  3846.   get_dimen (hdc, CAPS_HEIGHT, CAPS_VERTICAL_RESOLUTION,
  3847.              &result.height, &result.height_mm);
  3848.   send_answer (serial, &result, sizeof (result), 0);
  3849. }
  3850.  
  3851.  
  3852. /* Retrieve a text from the clipboard and send it to Emacs.  The text
  3853.    is in CR/LF format, without trailing null character.  The byte
  3854.    count as 32-bit number precedes the text.  If GET_TEXT is zero, the
  3855.    text isn't sent. */
  3856.  
  3857. static void get_clipboard (int serial, int get_text)
  3858. {
  3859.   ULONG size, osize, flag, ulData;
  3860.   PCH str, end;
  3861.   int opened;
  3862.  
  3863.   str = NULL; size = 0;
  3864.   opened = WinOpenClipbrd (hab);
  3865.   if (opened)
  3866.     {
  3867.       ulData = WinQueryClipbrdData (hab, CF_TEXT);
  3868.       /* Use DosQueryMem to cope with a missing null character. */
  3869.       osize = 0x7fffffff; flag = 0;
  3870.       if (ulData != 0 && DosQueryMem ((PVOID)ulData, &osize, &flag) == 0
  3871.           && (end = memchr ((PCH)ulData, 0, osize)) != NULL)
  3872.         {
  3873.           str = (PCH)ulData;
  3874.           size = end - str;
  3875.         }
  3876.     }
  3877.   if (get_text)
  3878.     {
  3879.       if (str != NULL)
  3880.         send_answer (serial, str, size, 0);
  3881.       else
  3882.         send_answer (serial, "", 0, 0);
  3883.     }
  3884.   else
  3885.     send_answer (serial, NULL, 0, size);
  3886.   if (opened)
  3887.     WinCloseClipbrd (hab);
  3888. }
  3889.  
  3890.  
  3891. /* Receive a buffer from Emacs and put the text into the clipboard.
  3892.    SIZE is the size of the text, in bytes.  The buffer is in CR/LF
  3893.    format, without trailing null character. */
  3894.  
  3895. static void put_clipboard (unsigned long size)
  3896. {
  3897.   char *buf;
  3898.  
  3899.   DosAllocSharedMem ((PVOID)&buf, NULL, size + 1,
  3900.                      PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_GIVEABLE);
  3901.   receive (buf, size);
  3902.   buf[size] = 0;
  3903.   if (WinOpenClipbrd (hab))
  3904.     {
  3905.       WinEmptyClipbrd (hab);
  3906.       WinSetClipbrdData (hab, (ULONG)buf, CF_TEXT, CFI_POINTER);
  3907.       WinCloseClipbrd (hab);
  3908.     }
  3909. }
  3910.  
  3911.  
  3912. /* Define a face. */
  3913.  
  3914. static void define_face (frame_data *f, pmr_face *pf, const char *name)
  3915. {
  3916.   face_data *face;
  3917.   font_spec spec;
  3918.   int i, gc;
  3919.  
  3920.   /* Use the default font of the frame if the font name cannot be
  3921.      parsed (or if it is empty). */
  3922.  
  3923.   if (!parse_font_spec (&spec, name))
  3924.       spec = f->font;
  3925.  
  3926.   /* Add the underline attribute to the font selection if the face is
  3927.      underlined. */
  3928.  
  3929.   if (pf->underline)
  3930.     spec.sel |= FATTR_SEL_UNDERSCORE;
  3931.  
  3932.   /* Search the face table for the face.  Set GC to the face number if
  3933.      found.  Set GC to 0 otherwise. */
  3934.  
  3935.   gc = 0;
  3936.   for (i = 1; i <= nfaces; ++i)
  3937.     if (face_vector[i].foreground == pf->foreground
  3938.         && face_vector[i].background == pf->background
  3939.         && equal_font_spec (&face_vector[i].spec, &spec))
  3940.       {
  3941.         gc = i;
  3942.         break;
  3943.       }
  3944.  
  3945.   /* Add the face if it is not in the face table. */
  3946.  
  3947.   if (gc == 0)
  3948.     {
  3949.       if (nfaces + 1 >= nfaces_allocated)
  3950.         {
  3951.           face_data *np;
  3952.  
  3953.           nfaces_allocated += 16;
  3954.           np = allocate (nfaces_allocated * sizeof (face_data));
  3955.           if (nfaces != 0)
  3956.             memcpy (np + 1, face_vector + 1, nfaces * sizeof (face_data));
  3957.           deallocate (face_vector);
  3958.           face_vector = np;
  3959.         }
  3960.       gc = ++nfaces;
  3961.       face = &face_vector[nfaces];
  3962.       face->spec = spec;
  3963.       face->foreground = pf->foreground;
  3964.       face->background = pf->background;
  3965.       face->font_id = 0;
  3966. #ifdef USE_PALETTE_MANAGER
  3967.       add_color (face->foreground);
  3968.       add_color (face->background);
  3969.       make_palette (f);
  3970. #endif
  3971.     }
  3972.  
  3973.   /* Send the face number to Emacs (which will use it as X graphics
  3974.      context). */
  3975.  
  3976.   send_answer (pf->serial, NULL, 0, gc);
  3977. }
  3978.  
  3979.  
  3980. /* Hide all windows and post a WM_QUIT message to the message queue of
  3981.    the PM thread.  We hide all windows with one call to avoid
  3982.    excessive repainting. */
  3983.  
  3984. static void terminate (void)
  3985. {
  3986.   frame_data *f;
  3987.   PSWP pswp;
  3988.   int i, j, n;
  3989.  
  3990.   terminating = TRUE;
  3991.   n = 0;
  3992.   for (i = 0; i < HASH_SIZE; ++i)
  3993.     for (f = hash_table[i]; f != NULL; f = f->next)
  3994.       ++n;
  3995.   if (n != 0)
  3996.     {
  3997.       pswp = allocate (n * sizeof (SWP));
  3998.       j = 0;
  3999.       for (i = 0; i < HASH_SIZE; ++i)
  4000.         for (f = hash_table[i]; f != NULL; f = f->next)
  4001.           if (j < n)
  4002.             {
  4003.               pswp[j].hwnd = f->hwndFrame;
  4004.               pswp[j].fl = SWP_HIDE;
  4005.               ++j;
  4006.             }
  4007.       WinSetMultWindowPos (hab, pswp, j);
  4008.       deallocate (pswp);
  4009.     }
  4010.   WinPostMsg (hwndObject, WM_QUIT, 0, 0);
  4011.  
  4012.   /* Sit for about 49 days to avoid reading from the pipe, which
  4013.      probably no longer exists now.  That is no longer true, as Emacs
  4014.      now waits until pmemacs.exe is dead, before closing the pipe.  */
  4015.  
  4016.   DosSleep ((ULONG)-1);
  4017. }
  4018.  
  4019.  
  4020. /* Build a font name in "Host Portable Character Representation".
  4021.    Here's an X11R4 example:
  4022.    -misc-fixed-bold-r-normal--13-100-100-100-c-80-iso8859-1 */
  4023.  
  4024. static int make_x_font_spec (unsigned char *dst, PFONTMETRICS pfm,
  4025.                              int bold, int italic, int size)
  4026. {
  4027.   return sprintf (dst, "-os2-%s-%s-%s-normal--%d-%d-%d-%d-m-1-cp850",
  4028.                   pfm->szFacename,
  4029.                   bold ? "bold" : "medium",
  4030.                   italic ? "i" : "r",
  4031.                   (int)pfm->lMaxCharInc,
  4032.                   size,
  4033.                   pfm->sXDeviceRes,
  4034.                   pfm->sYDeviceRes);
  4035. }
  4036.  
  4037.  
  4038. /* Build a font name in OS/2 format (size.name).  We extend the syntax
  4039.    to size[.bold][.italic]name. */
  4040.  
  4041. static int make_pm_font_spec (unsigned char *dst, PFONTMETRICS pfm,
  4042.                               int bold, int italic, int size)
  4043. {
  4044.   int i;
  4045.  
  4046.   i = sprintf (dst, "%d", (size + 5) / 10);
  4047.   if (bold)
  4048.     i += sprintf (dst+i, ".bold");
  4049.   if (italic)
  4050.     i += sprintf (dst+i, ".italic");
  4051.   i += sprintf (dst+i, ".%s", pfm->szFacename);
  4052.   return (i);
  4053. }
  4054.  
  4055.  
  4056. /* Pattern matching for listfont().  This should ignore letter case
  4057.    but doesn't now. */
  4058.  
  4059. static int font_match (const UCHAR *pattern, const UCHAR *name)
  4060. {
  4061.   for (;;)
  4062.     switch (*pattern)
  4063.       {
  4064.       case 0:
  4065.         return (*name == 0);
  4066.       case '?':
  4067.         ++pattern;
  4068.         if (*name == 0)
  4069.           return 0;
  4070.         ++name;
  4071.         break;
  4072.       case '*':
  4073.         while (*pattern == '*')
  4074.           ++pattern;
  4075.         if (*pattern == 0)
  4076.           return 1;
  4077.         while (*name != 0)
  4078.           {
  4079.             if (font_match (pattern, name))
  4080.               return 1;
  4081.             ++name;
  4082.           }
  4083.         return 0;
  4084.       default:
  4085.         if (*pattern != *name)
  4086.           return 0;
  4087.         ++pattern; ++name;
  4088.         break;
  4089.       }
  4090. }
  4091.  
  4092.  
  4093. /* Buffer for pm-list-fonts. */
  4094.  
  4095. static UCHAR *fontlist_buffer = NULL;
  4096. static ULONG fontlist_size = 0;
  4097. static ULONG fontlist_used = 0;
  4098.  
  4099. #define LISTFONT_PM     0x01    /* List PM variant only */
  4100. #define LISTFONT_X      0x02    /* List X variant only */
  4101. #define LISTFONT_NAME   0x04    /* Name is known */
  4102. #define LISTFONT_SIZE   0x08    /* Size is known */
  4103.  
  4104.  
  4105. static void fontlist_grow (ULONG incr)
  4106. {
  4107.   UCHAR *p;
  4108.  
  4109.   while (fontlist_used + incr > fontlist_size)
  4110.     {
  4111.       fontlist_size += 0x10000;
  4112.       p = allocate (fontlist_size);
  4113.       memcpy (p, fontlist_buffer, fontlist_used);
  4114.       deallocate (fontlist_buffer);
  4115.       fontlist_buffer = p;
  4116.     }
  4117. }
  4118.  
  4119.  
  4120. /* List one font for fontlist().  Reallocate the buffer if it's too
  4121.    small. */
  4122.  
  4123. static void list_one_font (pmd_fontlist *answer, PFONTMETRICS pfm,
  4124.                            int bold, int italic, int size,
  4125.                            const UCHAR *pattern, int flags)
  4126. {
  4127.   ULONG len;
  4128.   UCHAR *p;
  4129.  
  4130.   fontlist_grow (256);
  4131.   p = fontlist_buffer + fontlist_used;
  4132.   if (flags & LISTFONT_X)
  4133.     {
  4134.       len = make_x_font_spec (p + 1, pfm, bold, italic, size);
  4135.       if (font_match (pattern, p + 1))
  4136.         {
  4137.           *p = (UCHAR)len;
  4138.           fontlist_used += 1 + len;
  4139.           ++answer->count;
  4140.           p += 1 + len;
  4141.         }
  4142.     }
  4143.   if (flags & LISTFONT_PM)
  4144.     {
  4145.       len = make_pm_font_spec (p + 1, pfm, bold, italic, size);
  4146.       if (font_match (pattern, p + 1))
  4147.         {
  4148.           *p = (UCHAR)len;
  4149.           fontlist_used += 1 + len;
  4150.           ++answer->count;
  4151.         }
  4152.     }
  4153. }
  4154.  
  4155.  
  4156. /* Send a list of fonts to Emacs, for pm_list_fonts.  Try to optimize
  4157.    for speed by getting the name and size from the pattern.  If the
  4158.    size is not known, restrict the range of sizes for outline fonts to
  4159.    1 through 48pt, intersected with the range of sizes the font is
  4160.    designed for.  If the size is known, sizes greater than 48pt can be
  4161.    used. */
  4162.  
  4163. static void fontlist (frame_data *f, int serial, const UCHAR *pattern)
  4164. {
  4165.   pmd_fontlist answer;
  4166.   ULONG fonts, temp, i;
  4167.   PFONTMETRICS pfm;
  4168.   int bold, italic, size, flags;
  4169.   font_spec spec;
  4170.  
  4171.   temp = 0;
  4172.   fonts = GpiQueryFonts (f->hpsClient, QF_PUBLIC | QF_PRIVATE, NULL,
  4173.                          &temp, sizeof (FONTMETRICS), NULL);
  4174.   pfm = allocate (fonts * sizeof (FONTMETRICS));
  4175.   GpiQueryFonts (f->hpsClient, QF_PUBLIC | QF_PRIVATE, NULL,
  4176.                  &fonts, sizeof (FONTMETRICS), pfm);
  4177.   answer.count = 0;
  4178.   fontlist_used = 0;
  4179.   fontlist_grow (sizeof (answer));
  4180.   fontlist_used += sizeof (answer);
  4181.   flags = LISTFONT_PM | LISTFONT_X;
  4182.   if (pattern[0] == '-')
  4183.     flags &= ~LISTFONT_PM;
  4184.   if (pattern[0] >= '0' && pattern[0] <= '9')
  4185.     flags &= ~LISTFONT_X;
  4186.   if (parse_font_spec (&spec, pattern))
  4187.     {
  4188.       flags |= LISTFONT_SIZE;
  4189.       if (strpbrk (spec.name, "?*") == NULL)
  4190.         flags |= LISTFONT_NAME;
  4191.     }
  4192.   for (i = 0; i < fonts; ++i)
  4193.     if (f->var_width_fonts || (pfm[i].fsType & FM_TYPE_FIXED))
  4194.       if (!(flags & LISTFONT_NAME)
  4195.           || strcmp (pfm[i].szFacename, spec.name) == 0)
  4196.         for (bold = 0; bold <= 1; ++bold)
  4197.           for (italic = 0; italic <= 1; ++italic)
  4198.             if (pfm[i].fsDefn & FM_DEFN_OUTLINE)
  4199.               {
  4200.                 if (flags & LISTFONT_SIZE)
  4201.                   {
  4202.                     if (spec.size >= pfm[i].sMinimumPointSize
  4203.                         && spec.size <= pfm[i].sMaximumPointSize)
  4204.                       list_one_font (&answer, pfm + i, bold, italic,
  4205.                                      spec.size, pattern, flags);
  4206.                   }
  4207.                 else
  4208.                   for (size = 10 * ((pfm[i].sMinimumPointSize + 9) / 10);
  4209.                        size <= 480
  4210.                        && size <= pfm[i].sMaximumPointSize; size += 10)
  4211.                     list_one_font (&answer, pfm + i, bold, italic,
  4212.                                    size, pattern, flags);
  4213.               }
  4214.             else
  4215.               list_one_font (&answer, pfm + i, bold, italic,
  4216.                              pfm[i].sNominalPointSize, pattern, flags);
  4217.   memcpy (fontlist_buffer, &answer, sizeof (answer));
  4218.   send_answer (serial, fontlist_buffer, fontlist_used, 0);
  4219.   deallocate (pfm);
  4220. }
  4221.  
  4222.  
  4223. #define CURSOR_OFF(f) (void)((f)->cursor_on \
  4224.                              && WinShowCursor ((f)->hwndClient, FALSE))
  4225.  
  4226. #define CURSOR_BACK(f) (void)((f)->cursor_on \
  4227.                               && WinShowCursor ((f)->hwndClient, TRUE))
  4228.  
  4229.  
  4230.  
  4231. #define GROW_BUF(SIZE) do \
  4232.   if ((SIZE) > buf_size) \
  4233.     { \
  4234.       deallocate (buf); \
  4235.       buf_size = ((SIZE) + 0xfff) & ~0xfff; \
  4236.       buf = allocate (buf_size); \
  4237.     } \
  4238.   while (0)
  4239.  
  4240. /* Read the pipe.  This function is run in a secondary thread.  Here,
  4241.    we receive requests from Emacs. */
  4242.  
  4243. static void pipe_thread (ULONG arg)
  4244. {
  4245.   HMQ hmq;
  4246.   POINTL ptl, aptl[3];
  4247.   RECTL rcl, rcl2;
  4248.   SIZEF cbox;
  4249.   COLOR foreground, background;
  4250.   pm_request pmr;
  4251.   menu_data md;
  4252.   menubar_data mbd;
  4253.   frame_data *f;
  4254.   char *buf;
  4255.   ULONG buf_size, post_count;
  4256.   LONG cx;
  4257.   int font_id;
  4258.  
  4259.   hmq = WinCreateMsgQueue (hab, 50);
  4260.   buf_size = 512;
  4261.   buf = allocate (buf_size);
  4262.   for (;;)
  4263.     {
  4264.       receive (&pmr, sizeof (pmr));
  4265. #if 0
  4266.       sprintf (buf, "PMR %d", pmr.header.type);
  4267.       WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, buf, "debug", 0,
  4268.                      MB_MOVEABLE | MB_OK | MB_ICONEXCLAMATION);
  4269. #endif
  4270.       switch (pmr.header.type)
  4271.         {
  4272.         case PMR_CREATE:
  4273.           create_frame (&pmr.create);
  4274.           break;
  4275.  
  4276.         case PMR_CREATEDONE:
  4277.           f = find_frame (pmr.header.frame);
  4278.           if (f->initial_visibility > 0)
  4279.             initial_show_frame (f);
  4280.           else
  4281.             f->stage = STAGE_INVISIBLE;
  4282.           break;
  4283.  
  4284.         case PMR_DESTROY:
  4285.           destroy_frame (pmr.header.frame);
  4286.           break;
  4287.  
  4288.         case PMR_MODIFY:
  4289.           f = find_frame (pmr.header.frame);
  4290.           modify_frame (f);
  4291.           break;
  4292.  
  4293.         case PMR_GLYPHS:
  4294.           receive (buf, pmr.glyphs.count);
  4295.           f = find_frame (pmr.glyphs.header.frame);
  4296.           CURSOR_OFF (f);
  4297.           ptl.x = make_x (f, pmr.glyphs.x);
  4298.           ptl.y = make_y (f, pmr.glyphs.y);
  4299.  
  4300.           /* Note: pmr.glyphs.count must be <= 512 */
  4301.  
  4302.           /* Get the font ID for the face.  If no font has been
  4303.              created for that face, the font ID is zero.
  4304.              font_defined[0] is always zero, therefore make_font()
  4305.              will be called. */
  4306.           font_id = face_vector[pmr.glyphs.face].font_id;
  4307.           if (!f->font_defined[font_id])
  4308.             font_id = make_font (f, &face_vector[pmr.glyphs.face]);
  4309.           if (font_id != f->cur_charset)
  4310.             {
  4311.               GpiSetCharSet (f->hpsClient, font_id);
  4312.               f->cur_charset = font_id;
  4313.             }
  4314.           foreground = face_vector[pmr.glyphs.face].foreground;
  4315.           if (foreground != f->cur_color)
  4316.             {
  4317.               GpiSetColor (f->hpsClient, GET_COLOR (f->hpsClient, foreground));
  4318.               f->cur_color = foreground;
  4319.             }
  4320.           background = face_vector[pmr.glyphs.face].background;
  4321.           if (font_vector[font_id].use_incr)
  4322.             {
  4323.               if (f->cur_backmix != BM_LEAVEALONE)
  4324.                 {
  4325.                   GpiSetBackMix (f->hpsClient, BM_LEAVEALONE);
  4326.                   f->cur_backmix = BM_LEAVEALONE;
  4327.                 }
  4328.               if (font_vector[font_id].outline
  4329.                   && font_vector[font_id].cbox_size != f->cur_cbox_size)
  4330.                 {
  4331.                   cbox.cx = cbox.cy = font_vector[font_id].cbox_size;
  4332.                   GpiSetCharBox (f->hpsClient, &cbox);
  4333.                   f->cur_cbox_size = font_vector[font_id].cbox_size;
  4334.                 }
  4335.               rcl.xLeft = ptl.x;
  4336.               rcl.yBottom = ptl.y - f->cyDesc;
  4337.               rcl.xRight = rcl.xLeft + pmr.glyphs.count * f->cxChar;
  4338.               rcl.yTop = rcl.yBottom + f->cyChar;
  4339.               WinFillRect (f->hpsClient, &rcl,
  4340.                            GET_COLOR (f->hpsClient, background));
  4341.               --rcl.xRight; --rcl.yTop;
  4342.               GpiCharStringPosAt (f->hpsClient, &ptl, &rcl,
  4343.                                   CHS_VECTOR | CHS_CLIP,
  4344.                                   pmr.glyphs.count, buf, f->increments);
  4345.             }
  4346.           else
  4347.             {
  4348.               if (f->cur_backmix != BM_OVERPAINT)
  4349.                 {
  4350.                   GpiSetBackMix (f->hpsClient, BM_OVERPAINT);
  4351.                   f->cur_backmix = BM_OVERPAINT;
  4352.                 }
  4353.               if (background != f->cur_backcolor)
  4354.                 {
  4355.                   GpiSetBackColor (f->hpsClient,
  4356.                                    GET_COLOR (f->hpsClient, background));
  4357.                   f->cur_backcolor = background;
  4358.                 }
  4359.               GpiCharStringAt (f->hpsClient, &ptl, pmr.glyphs.count, buf);
  4360.             }
  4361.           CURSOR_BACK (f);
  4362.           break;
  4363.  
  4364.         case PMR_CLEAR:
  4365.           f = find_frame (pmr.glyphs.header.frame);
  4366.           CURSOR_OFF (f);
  4367.           rcl.xLeft = 0; rcl.xRight = f->cxClient;
  4368.           rcl.yBottom = 0; rcl.yTop = f->cyClient;
  4369.           WinFillRect (f->hpsClient, &rcl,
  4370.                        GET_COLOR (f->hpsClient, f->background_color));
  4371.           CURSOR_BACK (f);
  4372.           break;
  4373.  
  4374.         case PMR_CLREOL:
  4375.           f = find_frame (pmr.glyphs.header.frame);
  4376.           CURSOR_OFF (f);
  4377.           rcl.xLeft = make_x (f, pmr.clreol.x0);
  4378.           rcl.xRight = make_x (f, pmr.clreol.x1);
  4379.           rcl.yBottom = make_y (f, pmr.clreol.y) - f->cyDesc;
  4380.           rcl.yTop = rcl.yBottom + f->cyChar;
  4381.           WinFillRect (f->hpsClient, &rcl,
  4382.                        GET_COLOR (f->hpsClient, f->background_color));
  4383.           CURSOR_BACK (f);
  4384.           break;
  4385.  
  4386.         case PMR_LINES:
  4387.           f = find_frame (pmr.glyphs.header.frame);
  4388.           CURSOR_OFF (f);
  4389.           cx = f->cxClient - f->sb_width * f->cxChar;
  4390.           aptl[0].x = 0;
  4391.           aptl[1].x = cx;
  4392.           aptl[2].x = 0;
  4393.           rcl.xLeft = 0;
  4394.           rcl.xRight = cx;
  4395.           if (pmr.lines.count > 0)
  4396.             {
  4397.               aptl[0].y = make_y (f, pmr.lines.max_y);
  4398.               aptl[1].y = make_y (f, pmr.lines.y + pmr.lines.count);
  4399.               aptl[2].y = make_y (f, pmr.lines.max_y - pmr.lines.count);
  4400.               rcl.yBottom = make_y (f, pmr.lines.y + pmr.lines.count);
  4401.               rcl.yTop = make_y (f, pmr.lines.y);
  4402.             }
  4403.           else
  4404.             {
  4405.               aptl[0].y = make_y (f, pmr.lines.max_y + pmr.lines.count);
  4406.               aptl[1].y = make_y (f, pmr.lines.y);
  4407.               aptl[2].y = make_y (f, pmr.lines.max_y);
  4408.               rcl.yBottom = make_y (f, pmr.lines.max_y);
  4409.               rcl.yTop = make_y (f, pmr.lines.max_y + pmr.lines.count);
  4410.             }
  4411.           aptl[0].y += f->cyChar - f->cyDesc;
  4412.           aptl[1].y += f->cyChar - f->cyDesc;
  4413.           aptl[2].y += f->cyChar - f->cyDesc;
  4414.           rcl.yBottom += f->cyChar - f->cyDesc;
  4415.           rcl.yTop += f->cyChar - f->cyDesc;
  4416.  
  4417.           /* Use GpiBitblt if the source rectangle is completely
  4418.              visible.  Otherwise, repaint the target rectangle. */
  4419.  
  4420.           rcl2.xLeft = 0;
  4421.           rcl2.xRight = cx - 1;
  4422.           rcl2.yBottom = aptl[2].y;
  4423.           rcl2.yTop = aptl[2].y + aptl[1].y - aptl[0].y - 1;
  4424.           if (GpiRectVisible (f->hpsClient, &rcl2) == RVIS_VISIBLE)
  4425.             GpiBitBlt (f->hpsClient, f->hpsClient, 3, aptl, ROP_SRCCOPY,
  4426.                        BBO_IGNORE);
  4427.           else
  4428.             {
  4429.               rcl2.xLeft = 0; rcl2.xRight = cx;
  4430.               rcl2.yBottom = aptl[0].y; rcl2.yTop = aptl[1].y;
  4431.               WinInvalidateRect (f->hwndClient, &rcl2, FALSE);
  4432.             }
  4433.  
  4434.           /* Clear the lines uncovered. */
  4435.  
  4436.           WinFillRect (f->hpsClient, &rcl,
  4437.                        GET_COLOR (f->hpsClient, f->background_color));
  4438.           CURSOR_BACK (f);
  4439.           break;
  4440.  
  4441.         case PMR_CURSOR:
  4442.  
  4443.           /* Turn on or off cursor; change cursor position. */
  4444.  
  4445.           f = find_frame (pmr.cursor.header.frame);
  4446.           f->cursor_x = make_x (f, pmr.cursor.x);
  4447.           f->cursor_y = make_y (f, pmr.cursor.y) - f->cyDesc;
  4448.           f->cursor_on = pmr.cursor.on;
  4449.  
  4450.           if (has_focus (f))
  4451.             {
  4452.               if (f->cursor_on)
  4453.                 create_cursor (f);
  4454.               else
  4455.                 WinDestroyCursor (f->hwndClient);
  4456.             }
  4457.           break;
  4458.  
  4459.         case PMR_BELL:
  4460.  
  4461.           /* Sound the bell.  We don't want to wait until DosBeep()
  4462.              completes, therefore we delegate work to another thread.
  4463.              A visible bell is handled directly to avoid problems with
  4464.              multiple threads painting at the same time. */
  4465.  
  4466.           f = find_frame (pmr.bell.header.frame);
  4467.           if (pmr.bell.visible)
  4468.             {
  4469.               rcl.xLeft = f->cxClient / 4;
  4470.               rcl.xRight = rcl.xLeft + f->cxClient / 2;
  4471.               rcl.yBottom = f->cyClient / 4;
  4472.               rcl.yTop = rcl.yBottom + f->cyClient / 2;
  4473.               WinInvertRect (f->hpsClient, &rcl);
  4474.               DosSleep (150);
  4475.               WinInvertRect (f->hpsClient, &rcl);
  4476.             }
  4477.           else
  4478.             DosPostEventSem (hevBeep);
  4479.           break;
  4480.  
  4481.         case PMR_NAME:
  4482.           f = find_frame (pmr.name.header.frame);
  4483.           receive (buf, pmr.name.count);
  4484.           buf[pmr.name.count] = 0;
  4485.           WinSetWindowText (f->hwndFrame, buf);
  4486.           break;
  4487.  
  4488.         case PMR_VISIBLE:
  4489.           f = find_frame (pmr.visible.header.frame);
  4490.           if (f->stage == STAGE_CREATING)
  4491.             f->initial_visibility = pmr.visible.visible ? 1 : 0;
  4492.           else if (f->stage == STAGE_INVISIBLE)
  4493.             {
  4494.               f->initial_visibility = pmr.visible.visible ? 1 : 0;
  4495.               if (f->initial_visibility > 0)
  4496.                 initial_show_frame (f);
  4497.             }
  4498.           else if (pmr.visible.visible)
  4499.             WinSetWindowPos (f->hwndFrame, NULLHANDLE, 0, 0, 0, 0,
  4500.                              SWP_RESTORE | SWP_SHOW);
  4501.           else
  4502.             WinShowWindow (f->hwndFrame, FALSE);
  4503.           break;
  4504.  
  4505.         case PMR_FOCUS:
  4506.           f = find_frame (pmr.header.frame);
  4507.           WinSetActiveWindow (HWND_DESKTOP, f->hwndFrame);
  4508. #if 0
  4509.           WinSetWindowPos (f->hwndFrame, HWND_TOP, 0, 0, 0, 0, SWP_ZORDER);
  4510. #endif
  4511.           break;
  4512.  
  4513.         case PMR_ICONIFY:
  4514.           f = find_frame (pmr.header.frame);
  4515.           if (f->stage < STAGE_READY)
  4516.             f->initial_visibility = -1;
  4517.           else
  4518.             WinSetWindowPos (f->hwndFrame, NULLHANDLE, 0, 0, 0, 0,
  4519.                              SWP_MINIMIZE);
  4520.           break;
  4521.  
  4522.         case PMR_SIZE:
  4523.           f = find_frame (pmr.size.header.frame);
  4524.           f->width = pmr.size.width;
  4525.           f->height = pmr.size.height;
  4526.           set_size (f);
  4527.           break;
  4528.  
  4529.         case PMR_POPUPMENU:
  4530.           f = find_frame (pmr.popupmenu.header.frame);
  4531.           GROW_BUF (pmr.popupmenu.size);
  4532.           receive (buf, pmr.popupmenu.size);
  4533.           menu_serial = pmr.popupmenu.serial;
  4534.           md.button = pmr.popupmenu.button;
  4535.           md.align_top = pmr.popupmenu.align_top;
  4536.           md.x = make_x (f, pmr.popupmenu.x);
  4537.           md.y = make_y (f, pmr.popupmenu.y);
  4538.           md.data = (pm_menu *)buf;
  4539.           md.str = buf + pmr.popupmenu.count * sizeof (pm_menu);
  4540.           WinSendMsg (f->hwndClient, UWM_POPUPMENU,
  4541.                       MPFROMP (f), MPFROMP (&md));
  4542.           break;
  4543.  
  4544.         case PMR_MENUBAR:
  4545.           f = find_frame (pmr.menubar.header.frame);
  4546.           GROW_BUF (pmr.menubar.size);
  4547.           receive (buf, pmr.menubar.size);
  4548.           mbd.data = (pm_menu *)buf;
  4549.           mbd.str = buf + pmr.menubar.entries * sizeof (pm_menu);
  4550.           mbd.el_count = pmr.menubar.entries;
  4551.           mbd.str_bytes = (pmr.menubar.size
  4552.                            - pmr.menubar.entries * sizeof (pm_menu));
  4553.           DosResetEventSem (hevDone, &post_count);
  4554.           WinSendMsg (f->hwndClient, UWM_MENUBAR, MPFROMP (f), MPFROMP (&mbd));
  4555.  
  4556.           /* Wait until we are done updating the menubar to avoid
  4557.              overwriting `buf' while the other thread is still
  4558.              accessing it. */
  4559.  
  4560.           DosWaitEventSem (hevDone, SEM_INDEFINITE_WAIT);
  4561.           break;
  4562.  
  4563.         case PMR_MOUSEPOS:
  4564.           get_mousepos (pmr.mousepos.serial);
  4565.           break;
  4566.  
  4567.         case PMR_FRAMEPOS:
  4568.           f = find_frame (pmr.framepos.header.frame);
  4569.           get_framepos (f, pmr.framepos.serial);
  4570.           break;
  4571.  
  4572.         case PMR_PASTE:
  4573.           get_clipboard (pmr.paste.serial, pmr.paste.get_text);
  4574.           break;
  4575.  
  4576.         case PMR_CUT:
  4577.           put_clipboard (pmr.cut.size);
  4578.           break;
  4579.  
  4580.         case PMR_QUITCHAR:
  4581.           quit_char = pmr.quitchar.quitchar;
  4582.           break;
  4583.  
  4584.         case PMR_CLOSE:
  4585.           terminate ();
  4586.           break;
  4587.  
  4588.         case PMR_RAISE:
  4589.           f = find_frame (pmr.header.frame);
  4590.           WinSetWindowPos (f->hwndFrame, HWND_TOP, 0, 0, 0, 0, SWP_ZORDER);
  4591.           break;
  4592.  
  4593.         case PMR_LOWER:
  4594.           f = find_frame (pmr.header.frame);
  4595.           WinSetWindowPos (f->hwndFrame, HWND_BOTTOM, 0, 0, 0, 0, SWP_ZORDER);
  4596.           break;
  4597.  
  4598.         case PMR_FACE:
  4599.           f = find_frame (pmr.face.header.frame);
  4600.           receive (buf, pmr.face.name_length);
  4601.           buf[pmr.face.name_length] = 0;
  4602.           define_face (f, &pmr.face, buf);
  4603.           break;
  4604.  
  4605.         case PMR_FONTLIST:
  4606.           f = find_frame (pmr.fontlist.header.frame);
  4607.           receive (buf, pmr.fontlist.pattern_length);
  4608.           buf[pmr.fontlist.pattern_length] = 0;
  4609.           fontlist (f, pmr.fontlist.serial, buf);
  4610.           break;
  4611.  
  4612.         case PMR_TRACKMOUSE:
  4613.           track_mouse = pmr.track.flag;
  4614.           track_x = -1; track_y = -1;
  4615.           break;
  4616.  
  4617.         case PMR_SETPOS:
  4618.           f = find_frame (pmr.setpos.header.frame);
  4619.           set_pos (f, pmr.setpos.left, pmr.setpos.left_base,
  4620.                    pmr.setpos.top, pmr.setpos.top_base);
  4621.           break;
  4622.  
  4623.         case PMR_CONFIG:
  4624.           get_config (pmr.config.serial);
  4625.           break;
  4626.  
  4627.         case PMR_DIALOG:
  4628.           f = find_frame (pmr.dialog.header.frame);
  4629.           GROW_BUF (pmr.dialog.size);
  4630.           receive (buf, pmr.dialog.size);
  4631.           dialog_serial = pmr.dialog.serial;
  4632.           md.button = pmr.dialog.buttons;
  4633.           md.x = 0;
  4634.           md.y = 0;
  4635.           md.data = (pm_menu *)buf;
  4636.           md.str = buf + pmr.dialog.count * sizeof (pm_menu);
  4637.           DosResetEventSem (hevDone, &post_count);
  4638.           WinPostMsg (f->hwndClient, UWM_DIALOG,
  4639.                       MPFROMP (f), MPFROMP (&md));
  4640.  
  4641.           /* Wait until the dialog has been created to avoid
  4642.              overwriting `buf' while create_dialog() is still
  4643.              accessing it. */
  4644.  
  4645.           DosWaitEventSem (hevDone, SEM_INDEFINITE_WAIT);
  4646.           break;
  4647.  
  4648.         case PMR_DROP:
  4649.           get_drop (pmr.drop.cookie, pmr.drop.serial);
  4650.           break;
  4651.  
  4652.         case PMR_INITIALIZE:
  4653.           set_code_page (pmr.initialize.codepage, 0, FALSE);
  4654.           break;
  4655.  
  4656.         case PMR_CPLIST:
  4657.           cplist (pmr.cplist.serial);
  4658.           break;
  4659.  
  4660.         case PMR_CODEPAGE:
  4661.           set_code_page (pmr.codepage.codepage, pmr.codepage.serial, TRUE);
  4662.           break;
  4663.  
  4664.         case PMR_FILEDIALOG:
  4665.           f = find_frame (pmr.header.frame);
  4666.           GROW_BUF (sizeof (pm_filedialog));
  4667.           receive (buf, sizeof (pm_filedialog));
  4668.  
  4669.           DosResetEventSem (hevDone, &post_count);
  4670.           WinPostMsg (f->hwndClient, UWM_FILEDIALOG,
  4671.                       MPFROMP (f), MPFROMP (buf));
  4672.  
  4673.           /* Wait until the data has been copied to avoid overwriting
  4674.              `buf' while file_dialog() is still accessing it. */
  4675.  
  4676.           DosWaitEventSem (hevDone, SEM_INDEFINITE_WAIT);
  4677.           break;
  4678.  
  4679.     case PMR_CREATE_SCROLLBAR:
  4680.           f = find_frame (pmr.header.frame);
  4681.           GROW_BUF (sizeof (pm_create_scrollbar));
  4682.           receive (buf, sizeof (pm_create_scrollbar));
  4683.  
  4684.           DosResetEventSem (hevDone, &post_count);
  4685.           WinPostMsg (f->hwndClient, UWM_CREATE_SCROLLBAR,
  4686.                       MPFROMP (f), MPFROMP (buf));
  4687.  
  4688.           /* Wait until we are done creating the scroll bar to avoid
  4689.              overwriting `buf' while the other thread is still
  4690.              accessing it. */
  4691.  
  4692.           DosWaitEventSem (hevDone, SEM_INDEFINITE_WAIT);
  4693.           break;
  4694.  
  4695.         case PMR_UPDATE_SCROLLBAR:
  4696.           update_scrollbar (pmr.update_scrollbar.id,
  4697.                             &pmr.update_scrollbar.s);
  4698.           break;
  4699.  
  4700.         case PMR_MOVE_SCROLLBAR:
  4701.           f = find_frame (pmr.header.frame);
  4702.           move_scrollbar (f, pmr.move_scrollbar.id,
  4703.                           pmr.move_scrollbar.top,
  4704.                           pmr.move_scrollbar.left,
  4705.                           pmr.move_scrollbar.width,
  4706.                           pmr.move_scrollbar.height);
  4707.           break;
  4708.  
  4709.         case PMR_DESTROY_SCROLLBAR:
  4710.           f = find_frame (pmr.header.frame);
  4711.           WinPostMsg (f->hwndClient, UWM_DESTROY_SCROLLBAR,
  4712.                       0, MPFROMSHORT (pmr.destroy_scrollbar.id));
  4713.           break;
  4714.  
  4715.         default:
  4716.           pm_error ("Unknown message type");
  4717.         }
  4718.     }
  4719. }
  4720.  
  4721.  
  4722. /* Generate sound.  This function is run in a thread of its own.  As
  4723.    DosBeep() blocks until the previous DosBeep() ends, we donate a
  4724.    separate thread to calling it to avoid blocking the pipe. */
  4725.  
  4726. static void beep_thread (ULONG arg)
  4727. {
  4728.   ULONG count;
  4729.  
  4730.   for (;;)
  4731.     {
  4732.       DosWaitEventSem (hevBeep, SEM_INDEFINITE_WAIT);
  4733.       if (DosResetEventSem (hevBeep, &count) == 0)
  4734.         while (count > 0)
  4735.           {
  4736.             DosBeep (880, 50);  /* Same as WA_WARNING */
  4737.             --count;
  4738.           }
  4739.     }
  4740. }
  4741.  
  4742.  
  4743. /* Initializations. */
  4744.  
  4745. static void initialize (void)
  4746. {
  4747.   FONTMETRICS fm;
  4748.   int i;
  4749.  
  4750. #ifdef TRACE
  4751.   {
  4752.     ULONG rc, action;
  4753.  
  4754.     rc = DosOpen (TRACE_FNAME, &trace_handle, &action, 0, 0,
  4755.                   OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
  4756.                   (OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYNONE
  4757.                    | OPEN_FLAGS_NOINHERIT),
  4758.                   NULL);
  4759.     if (rc != 0)
  4760.       trace_handle = TRACE_OFF;
  4761.   }
  4762. #endif
  4763.  
  4764.   /* Obtain the width of an icon. */
  4765.  
  4766.   cxIcon = WinQuerySysValue (HWND_DESKTOP, SV_CXICON);
  4767.  
  4768.   /* Obtain the width and height of the default font, for converting
  4769.      to and from dialog box units. */
  4770.  
  4771.   hpsScreen = WinGetScreenPS (HWND_DESKTOP);
  4772.   GpiQueryFontMetrics (hpsScreen, (LONG)sizeof (fm), &fm);
  4773.   default_font_width = fm.lAveCharWidth;
  4774.   default_font_height = fm.lMaxBaselineExt;
  4775.  
  4776.   /* Create the event semaphore for telling pipe_thread() that the
  4777.      message sent to the main thread has been processed and the memory
  4778.      buffer is no longer required.  */
  4779.  
  4780.   DosCreateEventSem (NULL, &hevDone, 0, FALSE);
  4781.  
  4782.   /* Create a Mutex semaphore for protecting drop_list.  Allocate
  4783.      drop_list.  */
  4784.  
  4785.   DosCreateMutexSem (NULL, &drop_mutex, 0, FALSE);
  4786.   drop_list = allocate (DROP_MAX * sizeof (*drop_list));
  4787.  
  4788. #ifdef USE_PALETTE_MANAGER
  4789.   {
  4790.     HDC hdc;
  4791.     LONG caps;
  4792.  
  4793.     /* Check for palette manager. */
  4794.     hdc = GpiQueryDevice (hpsScreen);
  4795.     if (DevQueryCaps (hdc, CAPS_ADDITIONAL_GRAPHICS, 1, &caps)
  4796.         && (caps & CAPS_PALETTE_MANAGER))
  4797.       has_palette_manager = TRUE;
  4798.     color_table[colors++] = RGB_BLACK;
  4799.     color_table[colors++] = RGB_WHITE;
  4800.   }
  4801. #endif
  4802.  
  4803.   /* Initialize the table of scrollbars. */
  4804.  
  4805.   for (i = 0; i < MAX_SCROLLBARS; ++i)
  4806.     scrollbar_table[i].hwnd = NULLHANDLE;
  4807. }
  4808.  
  4809.  
  4810. /* Terminate the program. */
  4811.  
  4812. static void terminate_process (void)
  4813. {
  4814. #ifdef TRACE
  4815.   if (trace_handle != TRACE_OFF)
  4816.     {
  4817.       DosClose (trace_handle);
  4818.       trace_handle = TRACE_OFF;
  4819.     }
  4820. #endif
  4821.   WinDestroyMsgQueue (hmq);
  4822.   WinTerminate (hab);
  4823.   exit (0);
  4824. }
  4825.  
  4826.  
  4827. /* Entrypoint.  On the command line, three numbers are passed.  The
  4828.    first one is the process ID of Emacs, the other numbers are the
  4829.    file handles for the input and output pipes, respectively. */
  4830.  
  4831. int main (int argc, char *argv[])
  4832. {
  4833.   QMSG qmsg;
  4834.   TID tid;
  4835.   int i;
  4836.  
  4837.   /* Initialize the Presentation Manager and create a message queue
  4838.      for this thread, the PM thread. */
  4839.  
  4840.   hab = WinInitialize (0);
  4841.   hmq = WinCreateMsgQueue (hab, 50);
  4842.  
  4843.   /* Parse the command line. */
  4844.  
  4845.   if (argc - 1 != 3)
  4846.     pm_error ("Do not run pmemacs.exe manually; it is started by emacs.exe.");
  4847.   emacs_pid = atoi (argv[1+0]);
  4848.   inbound_pipe = atoi (argv[1+1]);
  4849.   outbound_pipe = atoi (argv[1+2]);
  4850.  
  4851.   /* Initialize the (hash) table of frames. */
  4852.  
  4853.   for (i = 0; i < HASH_SIZE; ++i)
  4854.     hash_table[i] = NULL;
  4855.  
  4856.   /* Obtain the size of the screen. */
  4857.  
  4858.   WinQueryWindowPos (HWND_DESKTOP, &swpScreen);
  4859.  
  4860.   /* Register window classes. */
  4861.  
  4862.   WinRegisterClass (hab, szClientClass, ClientWndProc,
  4863.                     CS_SIZEREDRAW | CS_MOVENOTIFY | CS_CLIPCHILDREN, 4L);
  4864.   WinRegisterClass (hab, szFrameClass, FrameWndProc, 0, 0L);
  4865.   WinRegisterClass (hab, szObjectClass, ObjectWndProc, 0, 0L);
  4866.  
  4867.   /* Create the object window.  See ObjectWndProc() for details. */
  4868.  
  4869.   hwndObject = WinCreateWindow (HWND_OBJECT, szObjectClass, "", 0,
  4870.                                 0, 0, 0, 0, NULLHANDLE, HWND_BOTTOM,
  4871.                                 0, NULL, NULL);
  4872.  
  4873.   initialize ();
  4874.  
  4875.   /* Create the event semaphore for triggering the sound thread. */
  4876.  
  4877.   DosCreateEventSem (NULL, &hevBeep, 0, FALSE);
  4878.  
  4879.   /* Start the threads used for reading the pipe and for beeping. */
  4880.  
  4881.   DosCreateThread (&tid, beep_thread, 0, 2, 0x8000);
  4882.   DosCreateThread (&tid, pipe_thread, 0, 2, 0x8000);
  4883.  
  4884.   /* This is the message loop of the PM thread.  This loop ends when
  4885.      receiving a WM_QUIT message. */
  4886.  
  4887.   while (WinGetMsg (hab, &qmsg, 0L, 0, 0))
  4888.     WinDispatchMsg (hab, &qmsg);
  4889.   terminate_process ();
  4890.   return (0);
  4891. }
  4892.