/*LINTLIBRARY*/

/*  @(#)xview.c 1.4 90/04/10
 *
 *  XView dependent graphics routines used by othello.
 *
 *  Copyright (c) - Rich Burridge - Sun Microsystems.
 *                                  All rights reserved.
 *
 *  Permission is given to distribute these sources, as long as the
 *  introductory messages are not removed, and no monies are exchanged.
 *
 *  No responsibility is taken for any errors or inaccuracies inherent
 *  either to the comments or the code of this program, but if reported
 *  to me, then an attempt will be made to fix them.
 */

#include "othello.h"
#include "color.h"
#include "extern.h"
#include "images.h"
#include <xview/xview.h>
#include <xview/canvas.h>
#include <xview/cms.h>
#include <xview/cursor.h>
#include <xview/svrimage.h>
#include <xview/xv_xrect.h>
#include <X11/Xlib.h>

#define  XV_SET       (void) xv_set
#define  WINDOW_DONE  (void) window_done

#define  BOLDFONT     "lucidasanstypewriter-bold-12"
#define  DEFFONT      "fixed"
#define  NORMALFONT   "lucidasanstypewriter-12"

int menu_proc() ;

mpr_static(icon_pr,  64, 64, 1, icon_image) ;
mpr_static(cicon_pr, 64, 64, 8, cicon_image) ;

Canvas canvas ;
Canvas_paint_window cpw ;
Event *cur_event ;
Frame frame ;
Icon othello_icon ;
Menu menus[MAXMENUS] ;
Notify_value destroy() ;
Server_image hglass_pr, nocur_pr ;
Xv_Cursor cursor[MAXCURSORS] ;

Display *dpy ;                  /* Display id of othello frame. */
Drawable images[MAXIMAGES] ;    /* Various graphics images used by othello. */
Drawable make_server_image() ;
Drawable xid ;                  /* Xid for othello canvas. */
GC gc ;                         /* Graphics context for text and lines. */
GC ropgc ;                      /* Graphics context for rops. */
GC stencilgc ;                  /* Graphics context for stencils. */
Window root ;
XFontStruct *font[MAXFONTS] ;   /* Xlib handles to the fonts. */
XGCValues gc_val ;              /* Used to setup graphics context values. */
int gc_flags ;                  /* Used to set up graphics context flags. */
int screen ;                    /* Default graphics display screen. */
unsigned long backgnd ;         /* Default background color. */
unsigned long foregnd ;         /* Default foreground color. */
unsigned long gc_mask ;         /* Mask for setting graphic context values. */
unsigned long palette[OTH_COLORSIZE] ;     /* Xlib color palette. */

int opvals[3] ;         /* Pixrect rasterop values. */
int started ;           /* Set just before window is displayed. */


batch(state)            /* Turn graphics batching on or off. */
enum bltype state ;
{
  XV_SET(xv_default_server, SERVER_SYNC, 1, 0) ;
}


/*ARGSUSED*/
void
canvas_proc(window, event)
Xv_window window ;
Event *event ;
{
  if (!started) return ;
  cur_event = event ;
  process_event() ;       /* Determine what kind of event it is. */
  handle_event() ;        /* And do the appropriate action. */
}


close_frame()        /* Iconise Othello window. */
{
  XV_SET(frame, FRAME_CLOSED, TRUE, 0) ;
}


color_area(x, y, width, height, color)
int x, y, width, height, color ;
{
  if (iscolor) gc_val.foreground = palette[color] ;
  else
    { 
      if (color == C_WHITE) gc_val.foreground = backgnd ;
      else gc_val.foreground = foregnd ;
    }
  gc_val.function = GXcopy ;
  gc_mask = GCForeground | GCFunction ;
  XChangeGC(dpy, gc, gc_mask, &gc_val) ;
  XFillRectangle(dpy, xid, gc,
                 x, y, (unsigned int) width, (unsigned int) height) ;
}


create_menu(mtype, values)    /* Create popup menus for cycle items. */
enum panel_type mtype ;
char *values[] ;
{
  int i = 0 ;
  int menuno ;     /* Current menu number. */
  int more = 1 ;   /* Cleared when current menu is complete.*/

  menuno = (int) mtype - (int) COMPUTER_PLAYS ;
  menus[menuno] = xv_create(XV_NULL,          MENU_COMMAND_MENU,
                            MENU_NOTIFY_PROC, menu_proc,
                            0) ;
  do
    {
      if (values[i] != NULL)
        XV_SET(menus[menuno], MENU_STRING_ITEM, values[i], i+1, 0) ;
      else more = 0 ;
      i++ ;
    }
  while (more) ;
}


destroy_frame()        /* Destroy Othello window. */
{
  WINDOW_DONE(frame) ;
}


do_menu(mtype)      /* Popup appropriate cycle menu and get value. */
enum item_type mtype ;
{
  int menuno ;

  menuno = (int) mtype - (int) COMPUTER_PLAYS ;
  menu_show(menus[menuno], canvas, cur_event, 0) ;
  return(0) ;
}


draw_image(x, y, width, height, image)
int x, y, width, height ;
enum image_type image ;
{
  gc_mask = GCStipple | GCTileStipXOrigin | GCTileStipYOrigin ;
  gc_val.stipple = images[(int) image] ;
  gc_val.ts_x_origin = x ;
  gc_val.ts_y_origin = y ;
  XChangeGC(dpy, ropgc, gc_mask, &gc_val) ;
  XFillRectangle(dpy, xid, ropgc, x, y, width, height) ;
}
 
 
draw_line(x1, y1, x2, y2, op, color)
int x1, y1, x2, y2, color ;
enum optype op ;
{
  if (iscolor) gc_val.foreground = palette[color] ;
  else         gc_val.foreground = foregnd ;
  gc_val.function = opvals[(int) op] ;
  XChangeGC(dpy, gc, GCForeground | GCFunction, &gc_val) ;
  XDrawLine(dpy, xid, gc, x1, y1, x2, y2) ;
}


draw_stencil(x, y, width, height, op, color, stencil, image)
int x, y, width, height, color ;
enum optype op ;
enum image_type stencil, image ;
{
  if (iscolor) gc_val.foreground = palette[color] ;
  else         gc_val.foreground = foregnd ;
  gc_val.function = opvals[(int) op] ;
  gc_val.clip_x_origin = x ;
  gc_val.clip_y_origin = y ;
  gc_val.clip_mask = images[(int) stencil] ;
  gc_val.stipple = images[(int) image] ;
  gc_val.ts_x_origin = x ;
  gc_val.ts_y_origin = y ;
  gc_mask = GCForeground | GCFunction | GCClipMask |
            GCClipXOrigin | GCClipYOrigin |
            GCStipple | GCTileStipXOrigin | GCTileStipYOrigin ;
  XChangeGC(dpy, stencilgc, gc_mask, &gc_val) ;
  XFillRectangle(dpy, xid, stencilgc, x, y, width, height) ;
}


draw_text(x, y, ftype, color, str)
enum font_type ftype ;
int x, y, color ;
char *str ;
{
  if (iscolor) gc_val.foreground = palette[color] ;
  else
    {
      if (color == C_WHITE) gc_val.foreground = backgnd ;
      else gc_val.foreground = foregnd ;
    }
  gc_val.font = font[(int) ftype]->fid ;
  gc_val.function = GXcopy ;
  XChangeGC(dpy, gc, GCFont | GCForeground | GCFunction, &gc_val) ;
  XDrawString(dpy, xid, gc, x, y, str, strlen(str)) ;
}


XFontStruct *
get_font(name)
char *name ;
{
  XFontStruct *f ;

  if (!(f = XLoadQueryFont(dpy, name)))
    if (!(f = XLoadQueryFont(dpy, DEFFONT)))
      {
        FPRINTF(stderr, "%s: couldn't get the default font.", progname) ;
        exit(1) ;
      }
  return(f) ;
}


get_strwidth(ftype, str)    /* Get width in pixels of string value. */
enum font_type ftype ;
char *str ;
{
  return(XTextWidth(font[(int) ftype], str, strlen(str))) ;
}


init_fonts()         /* Open the normal and bold fonts. */
{
  font[(int) BFONT] = get_font(BOLDFONT) ;
  font[(int) NFONT] = get_font(NORMALFONT) ;
  bfont_height = font[(int) BFONT]->max_bounds.ascent +
                 font[(int) BFONT]->max_bounds.descent ;
  nfont_height = font[(int) NFONT]->max_bounds.ascent +
                 font[(int) NFONT]->max_bounds.descent ;
}


init_ws_type()
{
  move_delta = 10 ;
  started = 0 ;       /* Kludge to correctly handle repaints. */
  return 0 ;
}


load_colors()      /* Create and load othello color map. */
{
  XColor ccol ;
  u_char red[OTH_COLORSIZE], green[OTH_COLORSIZE], blue[OTH_COLORSIZE] ;
  int i, numcolors ;

  if (iscolor)
    {
      oth_colorsetup(red, green, blue) ;
      numcolors = 0 ;
      for (i = 0; i < OTH_COLORSIZE; i++)
        {
          ccol.flags = DoRed | DoGreen | DoBlue ;
          ccol.red = (unsigned short) (red[i] << 8) ;
          ccol.green = (unsigned short) (green[i] << 8) ;
          ccol.blue = (unsigned short) (blue[i] << 8) ;
          if (XAllocColor(dpy, DefaultColormap(dpy, screen), &ccol) == True)
            palette[numcolors++] = ccol.pixel ;
        }
      if (numcolors < 2)
        {
          FPRINTF(stderr, "%s: cannot allocate colors.\n", progname) ;
          exit(1) ;
        }
    }
}


lock_screen(state)     /* Turn graphics locking on or off - null routine. */
enum bltype state ;
{}


make_canvas()               /* Create canvas for game board. */
{
  canvas = xv_create(frame,               CANVAS,
                     CANVAS_RETAINED,     FALSE,
                     OPENWIN_AUTO_CLEAR,  FALSE,
                     XV_HEIGHT,           TOTAL_HEIGHT,
                     XV_WIDTH,            TOTAL_WIDTH,
                     CANVAS_PAINTWINDOW_ATTRS,
                       WIN_CONSUME_EVENTS,
                         MS_LEFT, MS_MIDDLE, MS_RIGHT,
                         WIN_ASCII_EVENTS, KBD_USE, KBD_DONE,
                         LOC_MOVE, LOC_DRAG,
                         LOC_WINENTER, LOC_WINEXIT,
                         WIN_LEFT_KEYS, WIN_TOP_KEYS,
                         WIN_RIGHT_KEYS, WIN_REPAINT,
                         0,
                       WIN_EVENT_PROC,    canvas_proc,
                       0,
                     0) ;
  cpw = canvas_paint_window(canvas) ;
  cursor[(int) CANVASCUR] = xv_get(cpw, WIN_CURSOR) ;

  dpy = (Display *) xv_get(frame, XV_DISPLAY) ;
  xid = (Drawable) xv_get(cpw, XV_XID) ;

  screen = DefaultScreen(dpy) ;
  root = RootWindow(dpy, screen) ;
  foregnd = BlackPixel(dpy, screen) ;
  backgnd = WhitePixel(dpy, screen) ;

  gc_mask = GCForeground | GCBackground | GCGraphicsExposures ;
  gc_val.foreground = foregnd ;
  gc_val.background = backgnd ;
  gc_val.graphics_exposures = False ;
  gc = XCreateGC(dpy, root, gc_mask, &gc_val) ;

  ropgc = XCreateGC(dpy, root, gc_mask, &gc_val) ;
  XSetFillStyle(dpy, ropgc, FillStippled) ;

  stencilgc = XCreateGC(dpy, root, gc_mask, &gc_val) ;
  XSetFillStyle(dpy, stencilgc, FillOpaqueStippled) ;

  hglass_pr = xv_create(XV_NULL,           SERVER_IMAGE,
                        XV_WIDTH,          16,
                        XV_HEIGHT,         16,
                        SERVER_IMAGE_BITS, hglass_image,
                        0) ;
  cursor[(int) HOURGLASS] = xv_create(XV_NULL,      CURSOR,
                                      CURSOR_IMAGE, hglass_pr,
                                      0) ;
  nocur_pr = xv_create(XV_NULL,           SERVER_IMAGE,
                       XV_WIDTH,          16,
                       XV_HEIGHT,         16,
                       SERVER_IMAGE_BITS, nocur_image,
                       0) ;
  cursor[(int) NOCURSOR] = xv_create(XV_NULL,      CURSOR,
                                     CURSOR_IMAGE, nocur_pr,
                                     0) ;
  load_colors() ;

  images[(int) BUT_STENCIL] = make_server_image(button_stencil_image) ;
  images[(int) BUT_INVERT]  = make_server_image(button_invert_image) ;
  images[(int) BUT_NORMAL]  = make_server_image(button_normal_image) ;
  images[(int) CY_NORMAL]   = make_server_image(cycle_glyph_image) ;
  images[(int) CY_STENCIL]  = make_server_image(cycle_stencil_image) ;
  images[(int) CY_LINVERT]  = make_server_image(cycle_linvert_image) ;
  images[(int) CY_RINVERT]  = make_server_image(cycle_rinvert_image) ;
  images[(int) P_WHITE]     = make_server_image(white_image) ;
  images[(int) P_BLACK]     = make_server_image(black_image) ;
}


make_frame(argc, argv)      /* Create othello window. */
int argc ;
char *argv[] ;
{
  opvals[(int) RCLR] = GXclear ;
  opvals[(int) RSRC] = GXcopy ;
  opvals[(int) RINV] = GXxor ;

  xv_init(XV_INIT_ARGS, argc, argv, 0) ;
  frame = xv_create(XV_NULL,                     FRAME,
                    FRAME_ICON,                  othello_icon,
                    FRAME_LABEL,                 line,
                    FRAME_SHOW_RESIZE_CORNER,    FALSE,
                    FRAME_SUBWINDOWS_ADJUSTABLE, FALSE,
                    WIN_ERROR_MSG,               "Can't create window.",
                    0) ;
  iscolor = ((int) xv_get(frame, WIN_DEPTH) > 1) ? 1 : 0 ;
}


make_icon()
{
  othello_icon = xv_create(XV_NULL,    ICON,
                           ICON_IMAGE, &icon_pr,
                           0) ;
}


Drawable
make_server_image(image)
unsigned short image[] ;
{
  Server_image temp ;

  temp = xv_create(XV_NULL,           SERVER_IMAGE,
                   XV_WIDTH,          64,
                   XV_HEIGHT,         64,
                   SERVER_IMAGE_BITS, image,
                   0) ;
  return((int) xv_get(temp, XV_XID)) ;
}


menu_proc(menu, menu_item)
Menu menu ;
Menu_item menu_item ;
{
  int choice ;

  choice = (int) menu_get(menu_item, MENU_VALUE) ;
  if (choice) handle_item(choice - 1) ;
}


process_event()       /* Process the next canvas event. */
{
  int id ;

  id = (event_id(cur_event)) ;
  curx = event_x(cur_event) ;
  cury = event_y(cur_event) ;
 
       if (id == KBD_DONE    || id == LOC_WINEXIT) nextc = EXIT_WINDOW ;
  else if (id == LOC_WINENTER)                     nextc = ENTER_WINDOW ;
  else if (id == LOC_MOVE    || id == LOC_DRAG)    nextc = MOUSE_MOVING ;
  else if (id == WIN_REPAINT || id == WIN_RESIZE)  nextc = FRAME_REPAINT ;
  else if (id == WIN_REPAINT || id == WIN_RESIZE)  nextc = FRAME_REPAINT ;
  else if (event_is_button(cur_event) && event_is_down(cur_event))
    {
           if (id == MS_LEFT)   nextc = LEFT_DOWN ;
      else if (id == MS_MIDDLE) nextc = MIDDLE_DOWN ;
      else if (id == MS_RIGHT)  nextc = RIGHT_DOWN ;
    }
  else if (event_is_button(cur_event) && event_is_up(cur_event))
    {
           if (id == MS_LEFT)   nextc = LEFT_UP ;
      else if (id == MS_MIDDLE) nextc = MIDDLE_UP ;
      else if (id == MS_RIGHT)  nextc = RIGHT_UP ;
    }
  else if (event_is_ascii(cur_event))
    {
      cur_ch = id ;
      nextc = KEYBOARD ;
    }
}


set_cursor(ctype)
enum curtype ctype ;
{
  XV_SET(cpw, WIN_CURSOR, cursor[(int) ctype], 0) ;
  XV_SET(xv_default_server, SERVER_SYNC, 1, 0) ;
}


start_tool()      /* Display window and start the notifier. */
{
  if (iscolor)
    {
      othello_icon = (Icon) xv_get(frame, FRAME_ICON) ;
      XV_SET(othello_icon,
             ICON_IMAGE,   &cicon_pr,
             0) ;
      XV_SET(frame, FRAME_ICON, othello_icon, 0) ;
    }
  window_fit(frame) ;
  started = 1 ;
  xv_main_loop(frame) ;
}
