/*
   GSPLUS - Advanced Apple IIGS Emulator Environment
   Based on the KEGS emulator written by Kent Dickey
   See COPYRIGHT.txt for Copyright information
   See LICENSE.txt for license (GPL v2)
 */

#define WIN32_LEAN_AND_MEAN     /* Tell windows we want less header gunk */
#define STRICT                  /* Tell Windows we want compile type checks */

#include <windows.h>
#include <windowsx.h>
#include <shellapi.h>

#include "defc.h"
#include "protos.h"
#include "protos_windriver.h"
#include "adb_keycodes.h"

#include "win_keymap.h"

extern int Verbose;
extern int g_pause;
extern int g_warp_pointer;
extern int g_screen_depth;
extern Kimage g_mainwin_kimage;
extern word32 g_palette_8to1624[256];
extern word32 g_a2palette_8to1624[256];
extern int g_lores_colors[];
extern int g_installed_full_superhires_colormap;
extern int g_config_control_panel;

extern void quitEmulator();
extern void x_toggle_status_lines();
extern void x_show_console(int show);
extern void x_check_input_events();

int g_win_capslock_down = 0;
int g_screen_mdepth = 0;
int g_num_a2_keycodes = 0;
int g_win_button_states = 0;
int g_use_shmem = 1;
int g_screenshot_requested = 0; // DB to know if we want to save a screenshot

HDC g_main_cdc;
HWND g_hwnd_main;
BITMAPINFO *g_bmapinfo_ptr = 0;
volatile BITMAPINFOHEADER *g_bmaphdr_ptr = 0;

// KEGS32 specific customisations
int g_win_status_debug = 1;                             // Current visibility of status lines.
int g_win_status_debug_request = 1;             // Desired visibility of status lines.
int g_win_show_console = 0;                             // Current visibility of console.
int g_win_show_console_request = 1;             // Desired visibility of console.
RECT g_main_window_saved_rect;
int g_main_window_saved_style;
int g_main_window_saved_exstyle;
int g_win_fullscreen_state = 0;
#if 0 // enable non-integral full-screen scaling
        #define FULLSCREEN_SCALE_TYPE float
#else
        #define FULLSCREEN_SCALE_TYPE int
#endif
FULLSCREEN_SCALE_TYPE g_win_fullscreen_scale = 1;
int g_win_fullscreen_offsetx = 0;
int g_win_fullscreen_offsety = 0;

LPSTR g_clipboard;
size_t g_clipboard_pos;

int x_calc_ratio(float ratiox, float ratioy);


int win_update_mouse(int x, int y, int button_states, int buttons_valid)     {
  int buttons_changed;

#ifdef ACTIVEGS
  if (g_config_control_panel)           // OG ignore input events while in debugger
    return 0;

  buttons_valid &= 1;           // filter out middle & right button

#endif

  buttons_changed = ((g_win_button_states & buttons_valid) !=
                     button_states);
  g_win_button_states = (g_win_button_states & ~buttons_valid) |
                        (button_states & buttons_valid);
  if(g_warp_pointer && (x == A2_WINDOW_WIDTH/2) &&
     (y == A2_WINDOW_HEIGHT/2) && (!buttons_changed) ) {
    /* tell adb routs to recenter but ignore this motion */
    update_mouse(x, y, 0, -1);
    return 0;
  }
  return update_mouse(x, y, button_states, buttons_valid & 7);
}


// OG Added asynchronous key & mouse handler
#define ASYNCEVENT

#ifndef ASYNCEVENT
#define WIN_EVENT_MOUSE win_event_mouse
#define WIN_EVENT_KEY   win_event_key
#else
extern void add_event_mouse(int umsg,WPARAM wParam, LPARAM lParam);
extern void add_event_key(HWND hwnd, UINT raw_vk, BOOL down, int repeat, UINT flags);
#define WIN_EVENT_MOUSE add_event_mouse
#define WIN_EVENT_KEY   add_event_key
#endif

struct win32_mouse
{
  int umsg;
  int wparam;
  int lparam;
};

struct win32_key
{
  UINT raw_vk;
  BOOL down;
  int repeat;
  UINT flags;
};

int last_win32_mouse=0;
int next_win32_mouse=0;

int nb_win32_key=0;

#define MAX_EVENT 1024
struct win32_mouse win32_mouses[MAX_EVENT];
struct win32_key win32_keys[MAX_EVENT];

// OG Push Mouse Event

void add_event_mouse(int umsg,WPARAM wParam, LPARAM lParam) {

#ifdef ACTIVEGS
  if (g_config_control_panel)           // OG ignore input events while in debugger
    return;
#endif

  win32_mouses[next_win32_mouse].umsg = umsg;
  win32_mouses[next_win32_mouse].wparam = wParam;
  win32_mouses[next_win32_mouse].lparam = lParam;
  next_win32_mouse = (next_win32_mouse+1)%MAX_EVENT;

//	ASSERT (next_win32_mouse==last_win32_mouse)
}

// OG Push Key Event

void add_event_key(HWND hwnd, UINT raw_vk, BOOL down, int repeat, UINT flags)      {
#ifdef ACTIVEGS
  if (g_config_control_panel)           // OG ignore input events while in debugger
    return;
#endif

  if (nb_win32_key>=MAX_EVENT)
    return;

  win32_keys[nb_win32_key].raw_vk = raw_vk;
  win32_keys[nb_win32_key].down = down;
  win32_keys[nb_win32_key].repeat = repeat;
  win32_keys[nb_win32_key].flags = flags;

  nb_win32_key++;
}

void win_event_mouse(int umsg,WPARAM wParam, LPARAM lParam)      {
  POINT pt;
  word32 flags;
  int buttons;
  int x, y;
  int motion;

  x = LOWORD(lParam);
  y = HIWORD(lParam);


  // OG Reformat the mouse coordinates
  float ratiox = 0.0, ratioy = 0.0;
  if (!g_warp_pointer && x_calc_ratio(ratiox,ratioy))
  {
    x = (int)((float)(x)/ratiox);
    y = (int)((float)(y)/ratioy);
  }

  flags = wParam;
  x  -= BASE_MARGIN_LEFT;
  y -=  BASE_MARGIN_TOP;

  buttons = (flags & 1) +
            (((flags >> 1) & 1) << 2) +
            (((flags >> 4) & 1) << 1);
#if 0
  if (umsg!=WM_MOUSEMOVE)
    printf("Mouse at %d, %d fl: %08x, but: %d\n", x, y, flags, buttons);
#endif
  motion = win_update_mouse(x, y, buttons, 7);

  if(motion && g_warp_pointer) {
    /* move mouse to center of screen */
    pt.x = BASE_MARGIN_LEFT + A2_WINDOW_WIDTH/2;
    pt.y = BASE_MARGIN_TOP + A2_WINDOW_HEIGHT/2;
    ClientToScreen(g_hwnd_main, &pt);
    SetCursorPos(pt.x, pt.y);
  }
}

void win_event_key(HWND hwnd, UINT raw_vk, BOOL down, int repeat, UINT flags)      {
  word32 vk;
  int a2code;
  int is_up;
  int capslock_down;
  int i;

  if((flags & 0x4000) && down) {
    /* auto-repeating, just ignore it */
    return;
  }

  vk = raw_vk + (flags & 0x100);
#if 0
  printf("Key event, vk=%04x, down:%d, repeat: %d, flags: %08x\n",
         vk, down, repeat, flags);
#endif


  if((vk & 0xff) == VK_CAPITAL) {
    // Windows gives us up-and-down events of the actual key
    //  Use GetKeyState to get the true toggle state, and pass
    //  that on to the adb interface
    capslock_down = GetKeyState(VK_CAPITAL) & 0x01;
    if(capslock_down != g_win_capslock_down) {
      g_win_capslock_down = capslock_down;
      adb_physical_key_update(kVK_CapsLock, !capslock_down);
    }

    return;                     // Do no more processing!
  }

  /* search a2key_to_wsym to find wsym in col 1 or 2 */
  i = 0;
  is_up = !down;
  for(i = g_num_a2_keycodes-1; i >= 0; i--) {
    a2code = g_a2_key_to_wsym[i][0];
    if((vk == g_a2_key_to_wsym[i][1]) ||
       (vk == g_a2_key_to_wsym[i][2])) {
      vid_printf("Found vk:%04x = %02x\n", vk, a2code);
      adb_physical_key_update(a2code, is_up);
      return;
    }
  }
  printf("VK: %04x unknown\n", vk);
}

/* low-level hook for keyboard events, to bypass special handling of
 * windows key, alt-escape, etc
 */
LRESULT CALLBACK win_ll_keyboard(int nCode, WPARAM wParam, LPARAM lParam) {
  KBDLLHOOKSTRUCT *kb = (KBDLLHOOKSTRUCT *)lParam;
  int down = 0;

  if (nCode < 0 || GetFocus() == NULL) return CallNextHookEx(0, nCode, wParam, lParam);

  if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) down = 1;

  switch (kb->vkCode) {
    case VK_LWIN:
    case VK_RWIN:
    case VK_MENU:
    case VK_LMENU:
    case VK_RMENU:
    case VK_CONTROL:
    case VK_LCONTROL:
    case VK_RCONTROL:
      if (nb_win32_key < MAX_EVENT) {

        win32_keys[nb_win32_key].raw_vk = kb->vkCode;
        win32_keys[nb_win32_key].down = down;
        win32_keys[nb_win32_key].repeat = 0;
        win32_keys[nb_win32_key].flags = kb->flags; /* same? */

        nb_win32_key++;
      }
      return 1;
      break;
  }

  return CallNextHookEx(0, nCode, wParam, lParam);
}



void win_event_quit(HWND hwnd)      {
  quitEmulator();
}

extern int g_needfullrefreshfornextframe;

void win_event_redraw()      {
  g_needfullrefreshfornextframe = 1;
}

LRESULT CALLBACK win_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam)                  {
  int i;
  int numDraggedFiles;
  int szFilename;
  LPTSTR lpszFile;

  switch(umsg) {
    case WM_MOUSEMOVE:
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_MBUTTONDOWN:
    case WM_MBUTTONUP:
    case WM_RBUTTONDOWN:
    case WM_RBUTTONUP:
    case WM_LBUTTONDBLCLK:      // OG Added dblclk as WM_LBUTTONDOWN
      WIN_EVENT_MOUSE(umsg,wParam, lParam);
      return 0;
    case WM_PAINT:
      win_event_redraw();
      break;
    case WM_DROPFILES:
      numDraggedFiles = DragQueryFile((HDROP)wParam, 0xFFFFFFFF, NULL, 0);
      for (i = 0; i < numDraggedFiles; i++) {
        szFilename = DragQueryFile((HDROP)wParam, i, NULL, 0);
        lpszFile = (LPTSTR)malloc(szFilename + 1);
        szFilename = DragQueryFile((HDROP)wParam, i, lpszFile, szFilename + 1);
        cfg_inspect_maybe_insert_file(lpszFile, 0);
        free(lpszFile);
      }
      DragFinish((HDROP)wParam);
      break;
    case WM_KILLFOCUS:
      nb_win32_key = 0;
      adb_all_keys_up(); // ALT-TAB doesn't send key-up messages
      break;
    case WM_ACTIVATE:
      if (wParam == WA_INACTIVE) {
        nb_win32_key = 0;
        adb_all_keys_up();
      }
      break;
  }
  switch(umsg) {
    HANDLE_MSG(hwnd, WM_KEYUP, WIN_EVENT_KEY);
    HANDLE_MSG(hwnd, WM_KEYDOWN, WIN_EVENT_KEY);
    HANDLE_MSG(hwnd, WM_SYSKEYUP, WIN_EVENT_KEY);
    HANDLE_MSG(hwnd, WM_SYSKEYDOWN, WIN_EVENT_KEY);
    HANDLE_MSG(hwnd, WM_DESTROY, win_event_quit);
  }

  return DefWindowProc(hwnd, umsg, wParam, lParam);
}

void gsportinit(HWND _hwnd) {
  g_hwnd_main = _hwnd;

  HDC localdc = GetDC(g_hwnd_main);
  SetTextColor(localdc, RGB(0, 0, 0));
  SetBkColor(localdc, RGB(255, 255, 255));

  g_main_cdc = CreateCompatibleDC(localdc);
  ReleaseDC(g_hwnd_main,localdc);
}

void gsportshut() {
  if (g_main_cdc)
  {
    DeleteDC(g_main_cdc);
    g_main_cdc = NULL;
  }
  g_hwnd_main = NULL;
}

void check_input_events()      {
  x_check_input_events();

  // OG Unstack Mouse Events
  int i=last_win32_mouse;
  while(i!=next_win32_mouse)
  {
    win_event_mouse(win32_mouses[i].umsg,win32_mouses[i].wparam,win32_mouses[i].lparam);
    int lb = win32_mouses[i].wparam&1;
    i = (i+1)%MAX_EVENT;
    if (lb != (win32_mouses[i].wparam&1))
      break;                    // don't send up & down event at the same time
  }
  last_win32_mouse=i;

  for(i=0; i<nb_win32_key; i++)
    win_event_key(NULL,win32_keys[i].raw_vk,win32_keys[i].down,win32_keys[i].repeat,      win32_keys[i].flags );
  nb_win32_key=0;

  if (g_win_status_debug_request != g_win_status_debug)
    x_toggle_status_lines();
  if (g_win_show_console_request != g_win_show_console)
  {
    g_win_show_console = g_win_show_console_request;
    x_show_console(g_win_show_console_request);
  }
}

void x_update_color(int col_num, int red, int green, int blue, word32 rgb)      {
}

void x_update_physical_colormap()      {
}

void show_xcolor_array()      {
  int i;

  for(i = 0; i < 256; i++) {
    printf("%02x: %08x\n", i, g_palette_8to1624[i]);
  }
}

// OG Add function to clear all get_images loaded (dev dependent)
void x_release_kimage(Kimage *kimage_ptr) {
  if (kimage_ptr->dev_handle)
  {
    DeleteObject(kimage_ptr->dev_handle);
    kimage_ptr->dev_handle = NULL;
  }
  else if (kimage_ptr->data_ptr)
  {
    free(kimage_ptr->data_ptr);
    kimage_ptr->data_ptr = NULL;
  }
}

// OG  Free video global memory
void xdriver_end()      { // Should be renamed to dev_video_shut()  ???

  x_release_kimage(&g_mainwin_kimage);

  GlobalFree(g_bmapinfo_ptr);       // allocated in dev_video_init
  g_bmapinfo_ptr = 0;

  printf("win32 video driver end\n");
}

void x_get_kimage(Kimage *kimage_ptr)      {
  byte    *ptr;
  int width;
  int height;
  int depth, mdepth;
  int size;

  width = kimage_ptr->width_req;
  height = kimage_ptr->height;
  depth = kimage_ptr->depth;
  mdepth = kimage_ptr->mdepth;

  size = 0;
  if(depth == g_screen_depth) {
    /* Use g_bmapinfo_ptr, adjusting width, height */
    g_bmaphdr_ptr->biWidth = width;
    g_bmaphdr_ptr->biHeight = -height;

    HDC localdc = GetDC(g_hwnd_main);                   // OG Use on the fly DC
    kimage_ptr->dev_handle = CreateDIBSection(localdc,
                                              g_bmapinfo_ptr, DIB_RGB_COLORS,
                                              (VOID **)&(kimage_ptr->data_ptr), NULL, 0);
    ReleaseDC(g_hwnd_main,localdc);
  } else {
    /* allocate buffers for video.c to draw into */

    size = (width*height*mdepth) >> 3;
    ptr = (byte *)malloc(size);

    if(ptr == 0) {
      printf("malloc for data failed, mdepth: %d\n", mdepth);
      exit(2);
    }

    kimage_ptr->data_ptr = ptr;

    kimage_ptr->dev_handle = NULL;

  }
  //printf("kim: %p, dev:%p data: %p, size: %08x\n", kimage_ptr,
  //       kimage_ptr->dev_handle, kimage_ptr->data_ptr, size);

  return;
}

void dev_video_init()      {
  int extra_size;
  int lores_col;
  int i;

  printf("Preparing graphics system\n");

  // OG fix g_num_a2_keycodes identification
  g_num_a2_keycodes = sizeof(g_a2_key_to_wsym)/3/sizeof(int);
  g_num_a2_keycodes--;       // last entry=-1

  /*
     g_num_a2_keycodes = 0;
     for(i = 0; i < maxcode; i++) {
          int a2code = g_a2_key_to_wsym[i][0];
          if(a2code < 0) {
                  g_num_a2_keycodes = i;
          }
     }
   */

  // OG Forcing 16bits depth for WiNCE
#ifndef UNDER_CE
  g_screen_depth = 24;
  g_screen_mdepth = 32;
#else
  g_screen_depth = 16;
  g_screen_mdepth = 16;


  extern word32 g_red_mask;
  extern word32 g_green_mask;
  extern word32 g_blue_mask;
  extern int g_red_left_shift;
  extern int g_green_left_shift;
  extern int g_blue_left_shift;
  extern int g_red_right_shift;
  extern int g_green_right_shift;
  extern int g_blue_right_shift;


  g_red_mask = 0xff;
  g_green_mask = 0xff;
  g_blue_mask = 0xff;
  g_red_left_shift = 10;
  g_green_left_shift = 5;
  g_blue_left_shift = 0;
  g_red_right_shift = 3;
  g_green_right_shift = 3;
  g_blue_right_shift = 3;
#endif

  extra_size = sizeof(RGBQUAD);
  if(g_screen_depth == 8) {
    extra_size = 256 * sizeof(RGBQUAD);
  }
  g_bmapinfo_ptr = (BITMAPINFO *)GlobalAlloc(GPTR,
                                             sizeof(BITMAPINFOHEADER) + extra_size);

  g_bmaphdr_ptr = (BITMAPINFOHEADER *)g_bmapinfo_ptr;
  g_bmaphdr_ptr->biSize = sizeof(BITMAPINFOHEADER);
  g_bmaphdr_ptr->biWidth = A2_WINDOW_WIDTH;
  g_bmaphdr_ptr->biHeight = -A2_WINDOW_HEIGHT;
  g_bmaphdr_ptr->biPlanes = 1;
  g_bmaphdr_ptr->biBitCount = g_screen_mdepth;
  g_bmaphdr_ptr->biCompression = BI_RGB;
  g_bmaphdr_ptr->biClrUsed = 0;

  video_get_kimages();

  if(g_screen_depth != 8) {
    //	Allocate g_mainwin_kimage
    video_get_kimage(&g_mainwin_kimage, 0, g_screen_depth,
                     g_screen_mdepth);
  }

  for(i = 0; i < 256; i++) {
    lores_col = g_lores_colors[i & 0xf];
    video_update_color_raw(i, lores_col);
    g_a2palette_8to1624[i] = g_palette_8to1624[i];
  }

  g_installed_full_superhires_colormap = 1;

#ifndef UNDER_CE
  ShowWindow(g_hwnd_main, SW_SHOWDEFAULT);
#else
  ShowWindow(g_hwnd_main, SW_SHOW);
#endif
  UpdateWindow(g_hwnd_main);

  printf("Done with dev_video_init\n");
  fflush(stdout);
}

void x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy,
                   int width, int height) {
  void    *bitm_old;

  HDC localdc = GetDC(g_hwnd_main);
  HDC localcdc = g_main_cdc;

  bitm_old = SelectObject(localcdc, kimage_ptr->dev_handle);

  if (g_win_fullscreen_scale == 1)
    BitBlt(localdc, destx + g_win_fullscreen_offsetx, desty + g_win_fullscreen_offsety, width, height, localcdc, srcx, srcy, SRCCOPY);
  else
  {
    FULLSCREEN_SCALE_TYPE xdest = destx * g_win_fullscreen_scale + g_win_fullscreen_offsetx;
    FULLSCREEN_SCALE_TYPE ydest = desty * g_win_fullscreen_scale + g_win_fullscreen_offsety;
    FULLSCREEN_SCALE_TYPE wdest = width * g_win_fullscreen_scale;
    FULLSCREEN_SCALE_TYPE hdest = height * g_win_fullscreen_scale;

    StretchBlt(localdc, (int)xdest, (int)ydest, (int)wdest, (int)hdest, localcdc, srcx, srcy, width, height, SRCCOPY);
  }

  SelectObject(localcdc, bitm_old);
  ReleaseDC(g_hwnd_main,localdc);
}

void x_push_done()      {
}

void x_auto_repeat_on(int must)      {
}

void x_auto_repeat_off(int must)      {
}

void x_hide_pointer(int do_hide)      {
  if(do_hide) {
    ShowCursor(0);
  } else {
    ShowCursor(1);
  }
}

void x_full_screen(int do_full)      {
  MONITORINFO monitor_info;
  FULLSCREEN_SCALE_TYPE width, height, scalex, scaley;
  int top, left;

  g_win_status_debug = 1 - do_full;
  if (do_full && !g_win_fullscreen_state) {
    g_main_window_saved_style = GetWindowLong(g_hwnd_main, GWL_STYLE);
    g_main_window_saved_exstyle = GetWindowLong(g_hwnd_main, GWL_EXSTYLE);
    GetWindowRect(g_hwnd_main, &g_main_window_saved_rect);
    SetWindowLong(g_hwnd_main, GWL_STYLE, g_main_window_saved_style & ~(WS_CAPTION | WS_THICKFRAME));
    SetWindowLong(g_hwnd_main, GWL_EXSTYLE, g_main_window_saved_exstyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));

    monitor_info.cbSize = sizeof(monitor_info);
    GetMonitorInfo(MonitorFromWindow(g_hwnd_main, MONITOR_DEFAULTTONEAREST), &monitor_info);
    left = monitor_info.rcMonitor.left;
    top = monitor_info.rcMonitor.top;
    width = (FULLSCREEN_SCALE_TYPE)(monitor_info.rcMonitor.right - monitor_info.rcMonitor.left);
    height = (FULLSCREEN_SCALE_TYPE)(monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top);
    scalex = width / X_A2_WINDOW_WIDTH;
    scaley = height / X_A2_WINDOW_HEIGHT;
    g_win_fullscreen_scale = (scalex <= scaley) ? scalex : scaley;
    g_win_fullscreen_offsetx = ((int)width - (int)(g_win_fullscreen_scale * X_A2_WINDOW_WIDTH)) / 2;
    g_win_fullscreen_offsety = ((int)height - (int)(g_win_fullscreen_scale * X_A2_WINDOW_HEIGHT)) / 2;
    SetWindowPos(g_hwnd_main, NULL, left, top, (int)width, (int)height, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);

    g_win_fullscreen_state = 1;
    RedrawWindow(g_hwnd_main, NULL, NULL, RDW_INVALIDATE);


  } else {
    if (g_win_fullscreen_state) {
      g_win_fullscreen_offsetx = 0;
      g_win_fullscreen_offsety = 0;
      g_win_fullscreen_scale = 1;
      SetWindowLong(g_hwnd_main, GWL_STYLE, g_main_window_saved_style);
      SetWindowLong(g_hwnd_main, GWL_EXSTYLE, g_main_window_saved_exstyle);
      SetWindowPos(g_hwnd_main, NULL,
                   g_main_window_saved_rect.left,
                   g_main_window_saved_rect.top,
                   g_main_window_saved_rect.right - g_main_window_saved_rect.left,
                   g_main_window_saved_rect.bottom - g_main_window_saved_rect.top,
                   SWP_SHOWWINDOW);

      g_win_fullscreen_state = 0;
      RedrawWindow(g_hwnd_main, NULL, NULL, RDW_INVALIDATE);
    }
  }
  return;
}

void clipboard_paste()      {
  if (!IsClipboardFormatAvailable(CF_TEXT))
    return;
  if (!OpenClipboard(g_hwnd_main))
    return;
  {
    HGLOBAL hclipboard = GetClipboardData(CF_TEXT);
    if (!hclipboard) {
      CloseClipboard();
      return;
    }
    {
      LPSTR clipboard = (LPSTR)GlobalLock(hclipboard);
      if (!clipboard) {
        CloseClipboard();
        return;
      }
      if (g_clipboard) {
        free(g_clipboard);
        g_clipboard_pos = 0;
      }
      g_clipboard = strdup(clipboard);
    }
    GlobalUnlock(hclipboard);
  }
  CloseClipboard();
}

int clipboard_get_char()     {
  if (!g_clipboard)
    return 0;
  if (g_clipboard[g_clipboard_pos] == '\n')
    g_clipboard_pos++;
  if (g_clipboard[g_clipboard_pos] == '\0')
    return 0;
  return g_clipboard[g_clipboard_pos++] | 0x80;
}
