/* * * Copyright 1987, 1988, 1989, 1990 Ithaca Software, Alameda, CA * */

#include		"hoops.h"

#ifndef SUNVIEW

global boolean HD_Sunview_Driver (actor, request)
	stack		Driver			*actor;
	stack		int				request; {
	extern		boolean			HD_No_Driver ();

	return HD_No_Driver (actor, request, "Sunview");
}

#else

#include "database.h"
#include "please.h"
#include "patterns.h"
#include "driver.h"
#include "hidden.h"

#define Window Sunview_Window
#define Event  Sunview_Event
#undef	SUNVIEW
#	 include <stdio.h>
#	 include <sys/types.h>
#	 include <suntool/sunview.h>
#	 include <suntool/canvas.h>
#	 include <fcntl.h>
#	 include <sundev/kbio.h>
#	 include <sundev/kbd.h>
#	 include <sys/dir.h>
#	 include <pixrect/pixrect_hs.h>

#undef Event
#undef Window
#undef	SUNVIEW
#define SUNVIEW

#define NULL_PIXRECT ((Pixrect *) 0)
#define NULL_BRUSH	 ((struct pr_brush *) 0)
#define NULL_TEXTURE ((struct pr_texture *) 0)

#define COLOR_RESOLUTION				256
#define MAX_GRAY_LEVEL					((int) (1.33333 * DITHER_BIT_PATTERNS))
#define MAX_KEYSTROKES					256
#define MAX_COLORS						256
#define MAX_POLYLINE_POINTS				1000	/* DETERMINE FOR REAL! */
#define MAX_POLYGON_POINTS				128		/* DETERMINE FOR REAL! */
#define MAX_OPS							1500
#define DONT_EXIT						(-999)
#define MAX_FONTS						256

#define CHECK_LOCK(n, difficulty)								\
		if (sundata->screen_locked) {							\
			if ((n += difficulty) > sundata->max_ops) {					\
				pw_unlock (sundata->clipped_pw);						\
				pw_lock (sundata->clipped_pw, &sundata->windowinfo);	\
				n = 0;}}
		

#define Sunview_CONTROL_AREA			18
#define Sunview_BORDER					5

#define INDEX(bool, x, shift)					((bool) ? (x) : (x) << shift)

typedef struct {
	int			ascii;
	int			special;
}	Keyboard_Event;


typedef struct {
	Frame				frame;
	Canvas				canvas;
	Pixwin				*pw,
						*clipped_pw;
	int					keyboard_type;
	int					max_ops;
	unsigned char		upper_r[MAX_COLORS],
						upper_g[MAX_COLORS],
						upper_b[MAX_COLORS];
	unsigned char		*upper_red,
						*upper_green,
						*upper_blue;
	unsigned char		lower_r[MAX_COLORS],
						lower_g[MAX_COLORS],
						lower_b[MAX_COLORS];
	unsigned char		*lower_red,
						*lower_green,
						*lower_blue;
	Rect				windowinfo;
	Rect				created_windowinfo;
	char				colormap_name[32];
	struct pr_brush		brush,
						*p_brush;
	struct pr_texture	texture,
						*p_texture;	   
	unsigned char		fill_patterns[TOTAL_BIT_PATTERNS][64];
	struct pixrect		*color_pixrect;
	unsigned char		color_pattern[64];	  
	struct pixrect		*bw_pixrect;
	unsigned short		bw_pattern[16];
	Color_Index			index;
	struct pixrect		*pixrect;
	int					x_fudge,
						y_fudge;
	int					current_buttons;
	int					current_x, current_y;
	boolean				lower_colormap,
						repaint_in_progress;
	Keyboard_Event		kbd[MAX_KEYSTROKES],
						*first_kbd,
						*next_kbd;
	int					double_planes;
	int					total_planes;
	int					rounded_colors;
	int					reserved_colors;
	int					usable_colors;
	int					shift;
	int					lower_mask;
	int					upper_mask;
	Incarnation			last_rendition,
						last_clipping;
	int					ops;
	int					color;
	Color_Index			last_contrast_color;
	boolean				screen_locked;
	boolean				device_started;
	boolean				needs_repainting;
	boolean				was_resized;
	boolean				marked_for_delete;
}	SunData;			 

local	void			get_actions (),
						get_screen_dimensions(),
						all_events(),
						resize(),
						repaint();
local	boolean			create_frame_and_canvas(),
						is_suntools_running();
local	int				roundoff();
local	Notify_value	catch_quit();

extern void HD_Insert_In_Hidden_Tree();
extern void HD_See_Whats_Hidden();

global boolean HD_Sunview_Driver (actor, request)
	stack		Driver			*actor;
	stack		int				request; {
	extern		boolean			HD_Connect_Standard_Driver();

	return HD_Connect_Standard_Driver (actor, request, get_actions);
}

local void yawn () {}
 

local boolean start_device (dc) 
	stack		Display_Context			*dc; {
	register	SunData					*sundata;

	if (!is_suntools_running()) {
		HI_Error3 (
			"		 Sunview environment variables not defined			",
			"		   Sunview does not appear to be running			",
			"Hoops must be run from within the Sun windowing environment"); 
		return false;
	}

	ALLOC (sundata, SunData);
	dc->data = (void *) sundata;
	
	sundata->upper_red = &sundata->upper_r[0];
	sundata->upper_green = &sundata->upper_g[0];
	sundata->upper_blue = &sundata->upper_b[0];

	sundata->lower_red = &sundata->lower_r[0];
	sundata->lower_green = &sundata->lower_g[0];
	sundata->lower_blue = &sundata->lower_b[0];

	sundata->current_x = 0;
	sundata->current_y = 0;
	sundata->current_buttons = 0;

	sundata->frame = (Frame) -1;

	sundata->first_kbd = sundata->next_kbd = &sundata->kbd[0];

	sundata->lower_colormap = true;
	sundata->reserved_colors = 2;

	sundata->last_rendition = 0;
	sundata->last_clipping = 0;
	sundata->p_brush = NULL_BRUSH;
	sundata->p_texture = NULL_TEXTURE;

	sundata->repaint_in_progress = false;
	sundata->needs_repainting = false;

	sundata->last_contrast_color = -1;
	sundata->device_started = true;

	sundata->was_resized = false;
 
	sundata->marked_for_delete = false;
 
	return true;
}

local void get_physical_info (dc)
	stack		Display_Context			*dc; { 
	register	SunData					*sundata = (SunData *) dc->data;
	stack		int						keyboard;


	if ((keyboard = open ("/dev/kbd", O_RDONLY)) == -1)
	   sundata->keyboard_type = KB_SUN3;
	else {
		if (ioctl (keyboard, KIOCTYPE, &sundata->keyboard_type) == -1) 
			sundata->keyboard_type = KB_SUN3;
		close (keyboard);
	}

	dc->physical.absolute_coordinates = false;
	dc->physical.offset_in_pixels.x = 0;
	dc->physical.offset_in_pixels.y = 0;
	dc->physical.driver_type = "sunview";
	dc->physical.windowing_system = true;
	dc->physical.number_of_locater_buttons = 3;

	get_screen_dimensions (dc);

	dc->physical.can_double_buffer = true;

	dc->physical.first_color = 1;
	dc->physical.max_polygon_points = MAX_POLYGON_POINTS;
	dc->physical.max_polyline_points = MAX_POLYLINE_POINTS;
}

local void tell_hoops_about_canvas (dc, sundata) 
	stack		Display_Context			*dc; 
	register	SunData					*sundata; {
	stack		Pixwin					*pw;
	stack		int						lm, tm;
	stack		int						left, right,
										bottom, top;
	stack		Rect					r, *fr, *cr;
	register	int						ymax = dc->physical.number_of_pixels.y;
	stack		int						canvas_margin;
	stack		Rect					*open_frame;
	stack		int						window_type;


	if (sundata->frame == (Frame) -1) {
		window_type = (int) window_get (dc->options.use_window_ID, WIN_TYPE);

		if (window_type == CANVAS_TYPE)
			sundata->canvas = (Canvas) dc->options.use_window_ID;
		else  
			HI_Fatal ("Window ID does not point to a valid SunView canvas");
 
		sundata->frame = (Frame) window_get (sundata->canvas, WIN_OWNER);

		window_type = (int) window_get (sundata->frame, WIN_TYPE);
		if (window_type != FRAME_TYPE)
			HI_Fatal ("The canvas is not owned by a valid SunView frame");
	}

   fr = (Rect *) window_get (sundata->frame, FRAME_OPEN_RECT);
   lm = (int) window_get (sundata->frame, WIN_LEFT_MARGIN);
   tm = (int) window_get (sundata->frame, WIN_TOP_MARGIN);
   cr = (Rect *) window_get (sundata->canvas, WIN_RECT);

   pw = canvas_pixwin (sundata->canvas);

   pw_get_region_rect (pw, &r);
	   
   left = fr->r_left + lm + cr->r_left + r.r_left;
   right = left + r.r_width - 1;
   top = fr->r_left + tm + cr->r_top + r.r_top;
   bottom = top + r.r_height - 1;

   top = ymax - top;
   bottom = ymax - bottom;
 
   dc->options.subscreen_moving = true;
   dc->options.subscreen_resizing = true;
   dc->options.subscreen_stretching = true;

   HD_Resize_Subscreen (dc, true, left, right, bottom, top);
}

local void init_update (dc) 
	stack		Display_Context			*dc; {
	register	SunData					*sundata = (SunData *) dc->data;
	stack		Rect					*newrect;
	register	Rect					*oldrect;

	notify_dispatch();

	if (dc->options.use_window_ID != 0) {
		if (sundata->frame == (Frame) -1) {
			tell_hoops_about_canvas (dc, sundata);
		}
		else {
			newrect = (Rect *) window_get (sundata->frame, FRAME_OPEN_RECT);
			oldrect = &sundata->created_windowinfo;

			if (oldrect->r_top != newrect->r_top ||
				oldrect->r_left != newrect->r_left ||
				oldrect->r_width != newrect->r_width ||
				oldrect->r_height != newrect->r_height) {
				tell_hoops_about_canvas (dc, sundata);
			}
		}
	}
	else {
		if (sundata->frame != (Frame) -1 && sundata->was_resized) {
			newrect = (Rect *) window_get (sundata->frame, FRAME_OPEN_RECT);
			sundata->was_resized = false;
			oldrect = &sundata->created_windowinfo;
			sundata->windowinfo.r_left = newrect->r_left;
			sundata->windowinfo.r_top = newrect->r_top;
			sundata->windowinfo.r_width = newrect->r_width;
			sundata->windowinfo.r_height = newrect->r_height;

			if (oldrect->r_top != newrect->r_top ||
				oldrect->r_left != newrect->r_left ||
				oldrect->r_width != newrect->r_width ||
				oldrect->r_height != newrect->r_height) {
		sundata->created_windowinfo.r_left = newrect->r_left;
		sundata->created_windowinfo.r_top = newrect->r_top;
		sundata->created_windowinfo.r_width = newrect->r_width;
		sundata->created_windowinfo.r_height = newrect->r_height;

				HD_Resize_Subscreen (dc, true,
					newrect->r_left + Sunview_BORDER,
					newrect->r_left + newrect->r_width - 1 -
					Sunview_BORDER,
					dc->physical.number_of_pixels.y -
					(newrect->r_top - Sunview_BORDER +
					newrect->r_height - 1),
					dc->physical.number_of_pixels.y -
					(newrect->r_top + Sunview_CONTROL_AREA));
			}
		} 
	}
	sundata->needs_repainting = false;
	sundata->was_resized = false;
}

local void finish_update (dc) 
	stack		Display_Context			*dc; {
}

local void get_current_info (dc) 
	stack		Display_Context			*dc; {
	register	SunData					*sundata =(SunData *) dc->data;

	if (dc->options.use_window_ID == 0) {
		HD_Allow_For_Border (dc, 
			Sunview_BORDER, Sunview_BORDER, 
			Sunview_BORDER, Sunview_CONTROL_AREA);
	}
	else {
		dc->options.subscreen_moving = true;
		dc->options.subscreen_resizing = true;
		dc->options.subscreen_stretching = true;
	}


	sundata->rounded_colors = roundoff (dc->current.number_of_colors);
	sundata->shift = 0;

	if (sundata->total_planes == 1) {
		dc->options.double_buffering = false;
		sundata->rounded_colors = sundata->usable_colors = 2; 
		dc->current.number_of_colors = 2;
	}
	else if (dc->options.double_buffering) {
		register		int		total_colors = sundata->rounded_colors;

		if (total_colors == 256) {
			sundata->usable_colors = 16; 
			sundata->shift = 4;
			sundata->lower_mask = 0x000FL;
			sundata->upper_mask = 0x00F0L;
		}
		else if (total_colors == 128 || total_colors == 64) {
			sundata->usable_colors = 8; 
			sundata->shift = 3;
			sundata->lower_mask = 0x0007L;
			sundata->upper_mask = 0x0038L;
		}
		else if (total_colors == 32 || total_colors == 16) {
			sundata->usable_colors = 4; 
			sundata->shift = 2;
			sundata->lower_mask = 0x0003L;
			sundata->upper_mask = 0x000CL;
		}
		else {
			dc->options.double_buffering = false;
			sundata->usable_colors = total_colors; 
			sundata->shift = 0;
		}

#if 0
		if (total_colors == dc->physical.number_of_colors &&
			total_colors < dc->options.number_of_colors) {
				dc->current.number_of_colors = sundata->usable_colors;
				dc->current.first_color = 0;  
		}
		else {
#endif
			dc->current.number_of_colors = sundata->usable_colors - 
										   sundata->reserved_colors;
/*		}*/
	}
	else {
#if 0
		if (sundata->rounded_colors == dc->physical.number_of_colors &&
			sundata->rounded_colors <=	dc->options.number_of_colors) {
			sundata->usable_colors = sundata->rounded_colors;
			dc->current.first_color = 0;  
			dc->current.number_of_colors = sundata->usable_colors;
		}
		else {
#endif
			sundata->usable_colors = sundata->rounded_colors;
			dc->current.number_of_colors = sundata->usable_colors - 
										   sundata->reserved_colors;

/*		}*/
	}

	if (dc->options.debug > 1) {
		sundata->max_ops = dc->options.debug;
	}
	else
		sundata->max_ops = MAX_OPS;

}


local void stop_device (dc) 
	stack		Display_Context			*dc;  {
	register	SunData					*sundata =(SunData *) dc->data;

	if (dc->options.use_window_ID == 0) {
		/* free the clipping pixwin */
		pw_close (sundata->clipped_pw);

		window_set(sundata->canvas, WIN_CONSUME_PICK_EVENT, WIN_NO_EVENTS,
									WIN_CONSUME_KBD_EVENT,	WIN_NO_EVENTS,
									0);

		window_set(sundata->frame,	WIN_CONSUME_PICK_EVENT, WIN_NO_EVENTS,
									WIN_CONSUME_KBD_EVENT,	WIN_NO_EVENTS,
									0);

		window_set (sundata->frame, WIN_CLIENT_DATA, DONT_EXIT, 0);

		window_destroy (sundata->canvas);
		window_destroy (sundata->frame);
	}

	FREE (sundata, SunData);
}

local void resize_outer_window (dc) 
	stack		Display_Context			*dc; {
	register	SunData					*sundata =(SunData *) dc->data;
	register	Rect					tmp, *t;

	/* free the clipping pixwin */
	/* pw_close (sundata->clipped_pw); */

	if (dc->options.use_window_ID != 0) {
	}
	{
		stack			Int_Rectangle			*r = &dc->outer.extent;

		sundata->created_windowinfo.r_width = 
			dc->outer.extent.right - dc->outer.extent.left	+ 1 + 
			Sunview_BORDER * 2;
		sundata->created_windowinfo.r_height = 
			dc->outer.extent.top - dc->outer.extent.bottom + 1 + 
			Sunview_BORDER + Sunview_CONTROL_AREA;
		sundata->created_windowinfo.r_left = 
			dc->outer.extent.left - Sunview_BORDER;
		sundata->created_windowinfo.r_top = 
			dc->physical.number_of_pixels.y - dc->outer.extent.top -
			Sunview_CONTROL_AREA;

		if (dc->outer.extent.top + Sunview_CONTROL_AREA > 
			dc->physical.number_of_pixels.y) {
			sundata->created_windowinfo.r_top +=
				(r->top + Sunview_CONTROL_AREA) - 
				dc->physical.number_of_pixels.y;
		}
		if (dc->outer.extent.bottom - Sunview_BORDER < 0) {
			sundata->created_windowinfo.r_top -= Sunview_BORDER - r->bottom;
		}
		if (dc->outer.extent.left - Sunview_BORDER < 0) {
			sundata->created_windowinfo.r_left += Sunview_BORDER - r->left;
		}
		if (dc->outer.extent.right + Sunview_BORDER >
				 dc->physical.number_of_pixels.x) {
			sundata->created_windowinfo.r_left += 
				dc->physical.number_of_pixels.x - (r->right + Sunview_BORDER);
		}

		window_set (sundata->frame, 
			FRAME_OPEN_RECT, &sundata->created_windowinfo, 0);
	}

	/* re-create the clipping pixwin; "0,0,1,1" is arbitrary */
	sundata->clipped_pw = pw_region (sundata->pw, 0, 0, 1, 1);

	/* correct the clipping */
	sundata->last_rendition = sundata->last_clipping = 0;
}


local long get_outer_window (dc)
	stack		Display_Context			*dc; {
	register	SunData					*sundata = (SunData *) dc->data;
	register	int						i;
	stack		char					title[256];
	register	int						n;
	extern		int						getpid();

	if (dc->options.title.length != 0) 
		sprintf (title, "%0.*s", dc->options.title.length,
								 dc->options.title.text);
	else 
		sprintf (title, "%s", "Hoops Graphics System");

	{
		if (!create_frame_and_canvas (dc, title)) return -1;

		if (!sundata->marked_for_delete) {

			sundata->total_planes = sundata->pw->pw_pixrect->pr_depth;

			define_patterns (dc);

			if (sundata->total_planes == 1) 
				sundata->bw_pixrect = 
						mem_point (16, 16, 1, sundata->bw_pattern);
			else		
				sundata->color_pixrect = 
						mem_point(8, 8, 8, sundata->color_pattern);
		}

		/* we need to allocate a color map segment */
		/* initialize the color maps to all black, foreground white */
		sundata->lower_colormap = true;

		n = sundata->rounded_colors - 1;

		for (i = 0; i < n; ++i)	 {
			*sundata->upper_red++ = 0;
			*sundata->upper_green++ = 0;
			*sundata->upper_blue++ = 0;

			*sundata->lower_red++ = 0;
			*sundata->lower_green++ = 0;
			*sundata->lower_blue++ = 0;
		}

		*sundata->lower_red++ = *sundata->lower_green++ = 
		*sundata->lower_blue++ = COLOR_RESOLUTION - 1;

		*sundata->upper_red++ = *sundata->upper_green++ = 
		*sundata->upper_blue++ = COLOR_RESOLUTION - 1;

		sundata->upper_red = &sundata->upper_r[0];
		sundata->upper_green = &sundata->upper_g[0];
		sundata->upper_blue = &sundata->upper_b[0];

		sundata->lower_red = &sundata->lower_r[0];
		sundata->lower_green = &sundata->lower_g[0];
		sundata->lower_blue = &sundata->lower_b[0];

		if (!sundata->marked_for_delete) {
			/* create or reset color map segment with unique name */
			sprintf (sundata->colormap_name, "Hoops_%d", (int) getpid());
			pw_setcmsname(sundata->pw, sundata->colormap_name);
		
			pw_putcolormap(sundata->pw, 0, sundata->rounded_colors, 
				sundata->lower_red, sundata->lower_green, sundata->lower_blue);

			/* create the clipping pixwin; "0,0,1,1" is arbitrary */
			sundata->clipped_pw = pw_region (sundata->pw, 0, 0, 1, 1);
		}
		else {
			stack		Rect					rect;
  
			/* reset the clipping pixwin */ 
/*
			rect.r_top = 0;
			rect.r_left = 0;
			rect.r_height = 1;
			rect.r_width = 1;

			(void)pw_set_region_rect (sundata->clipped_pw, &rect, true);
*/
			pw_putcolormap(sundata->pw, 0, sundata->rounded_colors, 
				sundata->lower_red, sundata->lower_green, sundata->lower_blue);

			sundata->lower_mask = 0XFF;
			pw_putattributes (sundata->clipped_pw, &sundata->lower_mask);
		}
	}

	sundata->screen_locked = false;

	/* correct the clipping */
	sundata->last_rendition = sundata->last_clipping = 0;
  
	sundata->marked_for_delete = false;

	return (long) sundata->canvas;
}

local void free_outer_window (dc)
	stack		Display_Context			*dc; {
	register	SunData					*sundata =(SunData *) dc->data;
	register	int						i;

	sundata->marked_for_delete = true;
	sundata->lower_colormap = true;

}

local void init_picture (dc) 
	stack		Display_Context			*dc; {
	register	SunData					*sundata = (SunData *) dc->data;
	stack		Cursor					cursor;
	register	int						minusone = sundata->rounded_colors - 1;
	register	int						index;
	stack		unsigned char			red, green, blue;

	/* make sure the cursor is visible */
	index = dc->misc_rendition->window_contrast_color.map_index;		
	red = sundata->lower_red[index];
	green = sundata->lower_green[index];
	blue = sundata->lower_blue[index];

	if (dc->options.double_buffering) {
		sundata->lower_red[minusone] = sundata->lower_red[index];
		sundata->lower_green[minusone] = sundata->lower_green[index];
		sundata->lower_blue[minusone] = sundata->lower_blue[index];

		sundata->upper_red[minusone] = sundata->lower_red[index];
		sundata->upper_green[minusone] = sundata->lower_green[index];
		sundata->upper_blue[minusone] = sundata->lower_blue[index];

		if (sundata->lower_colormap) {
			pw_putattributes (sundata->clipped_pw, &sundata->lower_mask);
		}
		else {
			pw_putattributes (sundata->clipped_pw, &sundata->upper_mask);
		}
	}
	else {
		minusone = sundata->rounded_colors - 1;

		sundata->lower_red[minusone] = sundata->lower_red[index];
		sundata->lower_green[minusone] = sundata->lower_green[index];
		sundata->lower_blue[minusone] = sundata->lower_blue[index];
		pw_putcolormap(sundata->pw, minusone, 1, &red, &green, &blue);
	}

	sundata->ops = 1;
	
	cursor = window_get (sundata->canvas, WIN_CURSOR);
	cursor_set (cursor, CURSOR_SHOW_CURSOR, FALSE, 0);
	window_set (sundata->canvas, WIN_CURSOR, cursor, 0);

	if (!sundata->screen_locked) {
		pw_lock (sundata->clipped_pw, &sundata->windowinfo);
		sundata->screen_locked = true;
	}

}

local void finish_picture (dc, swap_buffers) 
	stack		Display_Context			*dc; 
	stack		boolean					swap_buffers; {
	register	SunData					*sundata = (SunData *) dc->data;
	stack		Cursor					cursor;
 
	if (swap_buffers) {
		if (sundata->lower_colormap) {
			pw_putcolormap(sundata->pw, 0, sundata->rounded_colors, 
						   sundata->lower_red, 
						   sundata->lower_green, 
						   sundata->lower_blue);
			sundata->lower_colormap = false;
		}
		else {
			pw_putcolormap(sundata->pw, 0, sundata->rounded_colors, 
						   sundata->upper_red, 
						   sundata->upper_green, 
						   sundata->upper_blue);
			sundata->lower_colormap = true;
		}
	}

	pw_reset(sundata->clipped_pw);
	cursor = window_get (sundata->canvas, WIN_CURSOR);
	cursor_set (cursor, CURSOR_SHOW_CURSOR, TRUE, 0);
	window_set (sundata->canvas, WIN_CURSOR, cursor, 0);

}

local void new_clipping (tr)
	register	Transform_Rendition		*tr; {
	register	SunData					*sundata = (SunData *)tr->h.data;
	stack		Rect					newrect;
   
 
	newrect.r_top = (tr->h.display_context->current.number_of_pixels.y - 1) -
					tr->hard_clip.top;
	newrect.r_left = tr->hard_clip.left;
	newrect.r_height = tr->hard_clip.top - tr->hard_clip.bottom + 1;
	newrect.r_width = tr->hard_clip.right - tr->hard_clip.left + 1;

	(void)pw_set_region_rect (sundata->clipped_pw, &newrect, true);
	
	sundata->x_fudge = -tr->hard_clip.left;
	sundata->y_fudge = tr->hard_clip.top;

	sundata->last_clipping = tr->h.incarnation;
}

local void draw_dc_rectangle (fr, left, right, bottom, top)
	register	Face_Rendition			*fr;
	stack		int						left, right, bottom, top; {
	register	SunData					*sundata = (SunData *)fr->h.data;
	stack		Display_Context			*dc = fr->h.display_context;
	stack		int						fore, 
										back;
				int						w, h;

	if (dc->physical.number_of_colors == 2) {
		if (fr->color.gray_level > MAX_GRAY_LEVEL * 0.5)
			fore = 1;
		else
			fore = 0;
	}
	else		
		fore = INDEX (sundata->lower_colormap, fr->color.map_index, sundata->shift);

	if (fr->h.transform_rendition->h.incarnation != sundata->last_clipping) 
		new_clipping (fr->h.transform_rendition);

	if (fr->pattern == FP_SOLID) {
		CHECK_LOCK(sundata->ops, 10);
		pw_rop (sundata->clipped_pw, 
				sundata->x_fudge + left, sundata->y_fudge - top, 
				right - left + 1, top - bottom + 1, 
				PIX_SRC | PIX_COLOR(fore), NULL_PIXRECT, 0, 0);
	}
	else {
		if (dc->physical.number_of_colors == 2) {
			register	unsigned short	*in,
										*out;
			stack		int				i = 16;

			out = sundata->bw_pattern;
			in = dc->bit_pattern[fr->pattern];
			while (--i >= 0) *out++ = *in++;

			CHECK_LOCK (sundata->ops, 10);
			pw_replrop (sundata->clipped_pw, 
						sundata->x_fudge + left, sundata->y_fudge - top, 
						right - left, top - bottom, PIX_SRC, 
						sundata->bw_pixrect, 0, 0);
		}
		else {
			register	unsigned char	*in;
			register	unsigned char	*out = &sundata->color_pattern[0];
			register	int				i = 8;
		   
			back = INDEX (sundata->lower_colormap, fr->contrast_color.map_index, 
															sundata->shift);
			
			in = &sundata->fill_patterns[fr->pattern][0];
			while (--i >= 0) {
				if (*in++ == 0) *out++ = fore; else *out++ = back;
				if (*in++ == 0) *out++ = fore; else *out++ = back;
				if (*in++ == 0) *out++ = fore; else *out++ = back;
				if (*in++ == 0) *out++ = fore; else *out++ = back;
				if (*in++ == 0) *out++ = fore; else *out++ = back;
				if (*in++ == 0) *out++ = fore; else *out++ = back;
				if (*in++ == 0) *out++ = fore; else *out++ = back;
				if (*in++ == 0) *out++ = fore; else *out++ = back;
			}			
			
			CHECK_LOCK (sundata->ops, 10);
			pw_replrop (sundata->clipped_pw, 
						sundata->x_fudge + left, sundata->y_fudge - top, 
						right - left, top - bottom, PIX_SRC, 
						sundata->color_pixrect, 0, 0);
		}
	}

	sundata->last_rendition = fr->h.incarnation;
}


local void draw_dc_overlay_polyline (lr, count, points, contrast_color)
	register	Line_Rendition			*lr;
	stack		int						count;
	stack		DC_Point				*points; 
	register	Driver_Color			*contrast_color; {
	register	SunData					*sundata = (SunData *)lr->h.data;
	stack		Display_Context			*dc = lr->h.display_context;
	stack		int						mode;

	/* force it to think about colors...? */
	if (sundata->last_contrast_color != contrast_color->map_index) {
		sundata->last_rendition = -1;
		sundata->last_contrast_color = contrast_color->map_index;
	}

	if (dc->physical.number_of_colors == 2) {	
		mode = PIX_NOT(PIX_DST);
		/*mode = PIX_SRC^PIX_DST;*/
		draw_dc_polyline_common (lr, count, points, mode);
	}
	else {
		lr->color.map_index ^= contrast_color->map_index;
		mode = PIX_SRC^PIX_DST;
		draw_dc_polyline_common (lr, count, points, mode);
		lr->color.map_index ^= contrast_color->map_index;
	}
   
}

local void draw_dc_polyline (lr, icount, points)
	stack		Line_Rendition			*lr;
	stack		int						icount;
	stack		DC_Point				*points; {
   
	draw_dc_polyline_common (lr, icount, points, PIX_SRC);
}

local void draw_dc_polyline_common (lr, count, points, mode)
	stack				Line_Rendition			*lr;
	stack				int						count;
	stack				DC_Point				*points; 
	stack				int						mode; {
	register			SunData					*sundata = (SunData *)lr->h.data;

	if (lr->h.incarnation != sundata->last_rendition)
		new_line_rendition (lr, mode);
		
	if (count == 2) {
		register		int						x0 = points[0].x;
		register		int						y0 = points[0].y;
		register		int						x1 = points[1].x;
		register		int						y1 = points[1].y;


		if (sundata->p_brush == NULL_BRUSH &&
			sundata->p_texture == NULL_TEXTURE) {

			CHECK_LOCK (sundata->ops, 1);
			pw_vector (sundata->clipped_pw, 
				sundata->x_fudge + x0, sundata->y_fudge - y0, 
				sundata->x_fudge + x1, sundata->y_fudge - y1, 
				mode, sundata->index);
		}
		else { 
			CHECK_LOCK (sundata->ops, 1);
			pw_line (sundata->clipped_pw, 
				sundata->x_fudge + x0, sundata->y_fudge - y0, 
				sundata->x_fudge + x1, sundata->y_fudge - y1, 
				sundata->p_brush, sundata->p_texture, 
				sundata->color);
		}
	}
	else {
		register		int				tmp = count;
		stack			struct pr_pos	pts[MAX_POLYLINE_POINTS],
										*p;

		p = &pts[0];

		do {
			p->x = sundata->x_fudge + points->x;
			p->y = sundata->y_fudge - points->y;
			++p;
			++points;
		}
		until (--tmp == 0);

		CHECK_LOCK (sundata->ops, 2);
		++sundata->ops;
		pw_polyline(sundata->clipped_pw, 0, 0, count, &pts[0], 0, 
				sundata->p_brush, sundata->p_texture, 
				mode | PIX_COLOR(sundata->index));
	}

}

local void new_line_rendition (lr, mode)
	register			Line_Rendition	*lr;  {
	register			SunData			*sundata = (SunData *)lr->h.data;
	stack				Display_Context *dc = lr->h.display_context;

	if (lr->pattern == FP_SOLID) {
		sundata->p_texture = NULL_TEXTURE;
	}
	else {
		switch (lr->pattern) {
			case LP_DASHDOT: sundata->texture.pattern = pr_tex_dashdot;			break;
			case LP_DASHED:	 sundata->texture.pattern = pr_tex_dashed;	break;
			case LP_DOTTED:	 sundata->texture.pattern = pr_tex_dotted;	break;
			default:	  HI_Internal_Data_Error ("Sunview(line pattern)");
						  break;
		}
		sundata->p_texture = &sundata->texture;
	}

	if (lr->weight == 1) 
		sundata->p_brush = NULL_BRUSH;
	else {
		sundata->p_brush = &sundata->brush;
		sundata->brush.width = lr->weight;
	}

	if (dc->physical.number_of_colors == 2) {
		if (lr->color.gray_level == 0) {
			sundata->index = 0;
			sundata->color = mode | PIX_COLOR(0);
		}
		else {
			sundata->index = 1;
			sundata->color = mode | PIX_COLOR(1);
		}
	}
	else {
		sundata->index = INDEX (sundata->lower_colormap, lr->color.map_index, 
								sundata->shift);

		sundata->color = mode | PIX_COLOR (sundata->index);
	}

	sundata->last_rendition = lr->h.incarnation;

	if (lr->h.transform_rendition->h.incarnation != sundata->last_clipping) 
		new_clipping (lr->h.transform_rendition);
}

local void draw_dc_overlay_face (fr, count, points, contrast_color)
	stack		Face_Rendition			*fr;
	stack		int						count;
	stack		DC_Point				*points;
	stack		Driver_Color			*contrast_color; {
	register	SunData					*sundata = (SunData *)fr->h.data;

	/* force it to think about colors...? */
	if (sundata->last_contrast_color != contrast_color->map_index) {
		sundata->last_rendition = 0;
		sundata->last_contrast_color = contrast_color->map_index;
	}

	fr->color.map_index ^= contrast_color->map_index;
	fr->contrast_color.map_index ^= contrast_color->map_index;
	draw_dc_face_common (fr, count, points, false, PIX_SRC ^ PIX_DST);
	fr->contrast_color.map_index ^= contrast_color->map_index;
	fr->color.map_index ^= contrast_color->map_index;
   
}

local void draw_dc_face (fr, icount, points)
	stack		Face_Rendition			*fr;
	stack		int						icount;
	stack		DC_Point				*points; {

	draw_dc_face_common (fr, icount, points, false, PIX_SRC);
}

local void draw_dc_polygon (fr, icount, points)
	stack		Face_Rendition			*fr;
	stack		int						icount;
	stack		DC_Point				*points; {

	draw_dc_face_common (fr, icount, points, true, PIX_SRC);
}


local void draw_dc_face_common (fr, count, points, edged, mode)
	stack				Face_Rendition	*fr;
	stack				int				count;
	stack				DC_Point		*points; 
	stack				boolean			edged; 
	stack				int				mode; {
	register			SunData			*sundata = (SunData *)fr->h.data;
	stack				int				npts;
	register			int				tmp = count;
	stack				struct pr_pos	pts[MAX_POLYGON_POINTS],
										*p;

	if (fr->h.incarnation != sundata->last_rendition) 
		new_face_rendition (fr, mode);

	/* don't explicitly close the polygon */	
	npts = count - 1;

	p = &pts[0];

	while (tmp-- > 0) {
		p->x = sundata->x_fudge + points->x;
		p->y = sundata->y_fudge - points->y;
		++p;
		++points;
	}

	CHECK_LOCK (sundata->ops, 5);
	pw_polygon_2 (sundata->clipped_pw, 0, 0, 1, &npts, &pts[0], 
				  sundata->color, sundata->pixrect, 0, 0);

	if (edged) {
		stack			Line_Rendition			*lr = fr->edge_rendition;
		
		if (lr->h.incarnation != sundata->last_rendition)
			new_line_rendition (lr, mode);
		
		CHECK_LOCK (sundata->ops, 2);
		pw_polyline(sundata->clipped_pw, 0, 0, count, &pts[0], 0, 
				sundata->p_brush, sundata->p_texture, 
				sundata->color);
	}
}

local void draw_dc_blended_face (fr, count, points,
								 color_count, colors, patterns)
	stack		Face_Rendition			*fr;
	stack		int						count;
	register	DC_Point				*points;
	stack		Color_Index				*colors;
	stack		int						*patterns; {
	register	SunData					*sundata = (SunData *)fr->h.data;
	stack		int						npts;
	register	int						tmp = count;
	stack		struct pr_pos			pts[MAX_POLYGON_POINTS],
										*p;
	stack		int						shift = sundata->shift;
	register	unsigned char			*in,
										*out;
	register	int						i;
	stack		int						cindex;
	stack		int						fore,
										back;
	npts = count;

	p = &pts[0];

	while (tmp-- > 0) {
		p->x = sundata->x_fudge + points->x;
		p->y = sundata->y_fudge - points->y;
		++p;
		++points;
	}

	/* set the first pattern */
	fore = INDEX (sundata->lower_colormap, colors[0], shift);
	back = INDEX (sundata->lower_colormap, colors[1], shift);
	in = &sundata->fill_patterns[*patterns][0];
	out = &sundata->color_pattern[0];
	i = 8;
	while (--i >= 0) {
		if (*in++ == 0) *out++ = fore; else *out++ = back;
		if (*in++ == 0) *out++ = fore; else *out++ = back;
		if (*in++ == 0) *out++ = fore; else *out++ = back;
		if (*in++ == 0) *out++ = fore; else *out++ = back;
		if (*in++ == 0) *out++ = fore; else *out++ = back;
		if (*in++ == 0) *out++ = fore; else *out++ = back;
		if (*in++ == 0) *out++ = fore; else *out++ = back;
		if (*in++ == 0) *out++ = fore; else *out++ = back;
	}
	sundata->color = PIX_SRC;
	sundata->pixrect = sundata->color_pixrect;
	++patterns;

   
	/* set up for the rest of the colors */
	cindex = 2;
	do {
		fore = INDEX (sundata->lower_colormap, colors[cindex], shift);
		in = &sundata->fill_patterns[*patterns][0];
		out = &sundata->color_pattern[0];
		i = 8;
		while (--i >= 0) {
			if (*in++ == 0) out++; else *out++ = fore;
			if (*in++ == 0) out++; else *out++ = fore;
			if (*in++ == 0) out++; else *out++ = fore;
			if (*in++ == 0) out++; else *out++ = fore;
			if (*in++ == 0) out++; else *out++ = fore;
			if (*in++ == 0) out++; else *out++ = fore;
			if (*in++ == 0) out++; else *out++ = fore;
			if (*in++ == 0) out++; else *out++ = fore;
		}
		++patterns;
	} until (++cindex == color_count);
	

	CHECK_LOCK (sundata->ops, 5);
	pw_polygon_2 (sundata->clipped_pw, 0, 0, 1, &npts, &pts[0], 
				  PIX_SRC, sundata->pixrect, 0, 0);
}

local void new_face_rendition (fr, mode)
	register			Face_Rendition	*fr;  
	stack				int				mode; {
	register			SunData			*sundata = (SunData *)fr->h.data;
	stack				Display_Context *dc = fr->h.display_context;
	register			int				fore,
										back;

	fore = INDEX (sundata->lower_colormap, fr->color.map_index, sundata->shift);

	if (fr->pattern == FP_SOLID && dc->physical.number_of_colors > 2) {
		sundata->color = mode | PIX_COLOR (fore);
		sundata->pixrect = NULL_PIXRECT;
	}
	else if (dc->physical.number_of_colors == 2) {
		register		unsigned short	*in;
		register		unsigned short	*out = sundata->bw_pattern;
		register		int				i = 16;
		stack			int				pattern_index = fr->pattern;
		stack			int				gray_level = fr->color.gray_level;

		if (gray_level == 0) {
			if (pattern_index == FP_SOLID) {
				sundata->color = mode | PIX_COLOR (0);
				sundata->pixrect = NULL_PIXRECT;
			}
			else {
				/* is this necessary? */
				in = dc->bit_pattern[pattern_index];
				while (--i >= 0) *out++ = *in++;
				sundata->color = mode;
				sundata->pixrect = sundata->bw_pixrect;
			}
		}
		else if (gray_level == MAX_GRAY_LEVEL - 1) {
			if (pattern_index == FP_SOLID) {
				sundata->color = mode | PIX_COLOR (1);
				sundata->pixrect = NULL_PIXRECT;
			}
			else {
				/* is this necessary? */
				in = dc->bit_pattern[pattern_index];
				while (--i >= 0) *out++ = ~*in++;
				sundata->color = mode;
				sundata->pixrect = sundata->bw_pixrect;
			}
		}
		else {
			/* does not do shaded patterned faces */
			if (fr->color.gray_level < .75 * MAX_GRAY_LEVEL) {
				pattern_index = fr->color.gray_level + FACE_BIT_PATTERNS; 
				in = dc->bit_pattern[pattern_index];
				while (--i >= 0) *out++ = *in++;
			}
			else {
				pattern_index = FACE_BIT_PATTERNS +	 
						(MAX_GRAY_LEVEL - gray_level);
				in = dc->bit_pattern[pattern_index];
				while (--i >= 0) *out++ = 0xFFFF - *in++;
			}
			sundata->color = mode;
			sundata->pixrect = sundata->bw_pixrect;
		}
	}
	else {
		register		unsigned char	*in = &sundata->fill_patterns[fr->pattern][0];
		register		unsigned char	*out = &sundata->color_pattern[0];
		register		int				i = 8;

		back = INDEX (sundata->lower_colormap, fr->contrast_color.map_index, sundata->shift);
		 
		while (--i >= 0) {
			if (*in++ == 0) *out++ = fore; else *out++ = back;
			if (*in++ == 0) *out++ = fore; else *out++ = back;
			if (*in++ == 0) *out++ = fore; else *out++ = back;
			if (*in++ == 0) *out++ = fore; else *out++ = back;
			if (*in++ == 0) *out++ = fore; else *out++ = back;
			if (*in++ == 0) *out++ = fore; else *out++ = back;
			if (*in++ == 0) *out++ = fore; else *out++ = back;
			if (*in++ == 0) *out++ = fore; else *out++ = back;
		}
		sundata->color = mode;
		sundata->pixrect = sundata->color_pixrect;
	}

	sundata->last_rendition = fr->h.incarnation;

	if (fr->h.transform_rendition->h.incarnation != sundata->last_clipping) 
		new_clipping (fr->h.transform_rendition);
}

#define KBD_SHIFT		0x1
#define KBD_CONTROL		0x2
#define KBD_META		0x4
#define ESCAPE_CHARACTER 27
#define RIGHT_BRACKET	 91

local boolean request_keyboard (dc, button, status) 
	stack		Display_Context			*dc; 
	stack		int						*button, *status; {
	register	SunData					*sundata =(SunData *) dc->data;

	notify_dispatch();
	if (sundata->needs_repainting) {
		HD_Force_Refresh (dc);
	}

	if (sundata->first_kbd < sundata->next_kbd) {
		register		int		vbutton = sundata->first_kbd->ascii;

		if (vbutton >= KEY_LEFTFIRST && vbutton <= KEY_LEFTLAST) 
			*button = FIRST_FUNCTION_KEY + vbutton - KEY_LEFTFIRST + 1;			
		else if (vbutton >= KEY_RIGHTFIRST && vbutton <= KEY_RIGHTLAST) {
			if (sundata->keyboard_type == KB_SUN4) {
				/* done for new keypad on 386i */
				register		int				stroke = vbutton - KEY_RIGHTFIRST + 1;
				switch (stroke) {
					case 14:	*button = DOWN_ARROW_KEY;		break;
					case 10:	*button = LEFT_ARROW_KEY;		break;
					case 12:	*button = RIGHT_ARROW_KEY;		break;
					case  8:	*button = UP_ARROW_KEY;			break;
					default: {
						*button = FIRST_KEYPAD_KEY + vbutton - KEY_RIGHTFIRST + 1; 
					}	 break;
				}
			}
		}
		else if (vbutton >= KEY_TOPFIRST && vbutton <= KEY_TOPLAST) 
			*button = FIRST_KEYPAD_KEY + 15 + vbutton - KEY_TOPFIRST + 1;		
		else if (vbutton == ESCAPE_CHARACTER) {
			stack		int				n;
			stack		Keyboard_Event	*first_and_one = sundata->first_kbd + 1;
			stack		Keyboard_Event	*first_and_two = sundata->first_kbd + 2;

			notify_dispatch(); notify_dispatch();
			n = sundata->next_kbd - sundata->first_kbd;

			if (n >= 3 && first_and_one->ascii == RIGHT_BRACKET) {
				switch (first_and_two->ascii) {
					case 65: {
						*button = UP_ARROW_KEY; 
						sundata->first_kbd += 3;		
					}	break;
					case 66: {
						*button = DOWN_ARROW_KEY;		
						sundata->first_kbd += 3;		
					}	break;
					case 67: {
						*button = RIGHT_ARROW_KEY;		
						sundata->first_kbd += 3;		
					}	break;
					case 68: {
						*button = LEFT_ARROW_KEY;		
						sundata->first_kbd += 3;		
					}	break;
					default: {
						*button = ESCAPE_CHARACTER;
						++sundata->first_kbd;
					}	break;
				}
			}
			else {
				*button = ESCAPE_CHARACTER;
				++sundata->first_kbd;
			}
			return true;
		}
		else	
			*button = sundata->first_kbd->ascii;
		
		*status = sundata->first_kbd->special;
		++sundata->first_kbd;
		return true;
	}
	else {
		sundata->first_kbd = sundata->next_kbd = &sundata->kbd[0];
		return false;
	}
}

local boolean request_location (dc, button, x, y)
	stack		Display_Context			*dc; 
	stack		int						*button;
	stack		int						*x, *y; {
	register	SunData					*sundata =(SunData *) dc->data;

	notify_dispatch();
	if (sundata->needs_repainting) HD_Force_Refresh (dc);

	*button = sundata->current_buttons;
	*x = sundata->current_x;
	*y = dc->current.number_of_pixels.y - 1 - sundata->current_y;

	return true;
}

local void set_device_color_map (dc, r, g, b, index) 
	stack		Display_Context			*dc;
	stack		int						r, g, b;
	stack		int						index; {
	stack		int						tmp = index;
	stack		SunData					*sundata =(SunData *) dc->data;
	stack		unsigned char			local_r = r,
										local_g = g,
										local_b = b;
	register	int						i;

	if (dc->options.double_buffering) {
		register		int		entries = 1 << sundata->shift;
		   for (i = 0; i < entries; ++i) {
				sundata->lower_red[tmp] = local_r;
				sundata->lower_green[tmp] = local_g;
				sundata->lower_blue[tmp] = local_b;
				tmp += entries;
			}
			tmp = index * ( 1 << sundata->shift);
			for (i = 0; i < entries; ++i) {
				sundata->upper_red[tmp] = local_r;
				sundata->upper_green[tmp] = local_g;
				sundata->upper_blue[tmp] = local_b;
				++tmp;
			}
	}
	else {
		sundata->lower_red[index] = local_r;
		sundata->lower_green[index] = local_g;
		sundata->lower_blue[index] = local_b;
		pw_putcolormap (sundata->pw, index, 1, &local_r, &local_g, &local_b);
	}
}

local void draw_raster (mr, where, count, craster) 
	stack		Misc_Rendition			*mr; 
	stack		DC_Point				*where;
	stack		int						count; 
	stack		char					*craster; {
	stack		SunData					*sundata =(SunData *)mr->h.data;

	/* the following is an expansion of the "mpr_static" macro, only
	 * done for stack variables. (Can't use the macro directly because
	 * the compiler doesn't support auto struct initializations.)
	 *
	 * (the next best way of doing this - the mem_point routine - allocates
	 *	memory that we don't know how to free.)
	 */

	stack		struct mpr_data			mpr;
	extern		struct pixrectops		mem_ops;
	stack		Pixrect					pr;

	mpr.md_linebytes = mpr_linebytes (count, 8);
	mpr.md_image = (short *)craster;
	mpr.md_offset.x = 0;
	mpr.md_offset.y = 0;
	mpr.md_primary = 0;
	mpr.md_flags = 0;

	pr.pr_ops = &mem_ops;
	pr.pr_size.x = count;
	pr.pr_size.y = 1;
	pr.pr_depth = 8;
	pr.pr_data = (caddr_t)&mpr;

	CHECK_LOCK (sundata->ops, 10);
	pw_rop (sundata->clipped_pw, 
			sundata->x_fudge + where->x, sundata->y_fudge - where->y, 
			count, 1, PIX_SRC, (&pr), 0, 0);
}

local void draw_bit_raster (mr, where, count, craster) 
	stack		Misc_Rendition			*mr; 
	stack		DC_Point				*where;
	stack		int						count; 
	stack		char					*craster; {
	stack		SunData					*sundata =(SunData *)mr->h.data;

	/* the following is an expansion of the "mpr_static" macro, only
	 * done for stack variables. (Can't use the macro directly because
	 * the compiler doesn't support auto struct initializations.)
	 *
	 * (the next best way of doing this - the mem_point routine - allocates
	 *	memory that we don't know how to free.)
	 */

	stack		struct mpr_data			mpr;
	extern		struct pixrectops		mem_ops;
	stack		Pixrect					pr;

	mpr.md_linebytes = mpr_linebytes (count, 1);
	mpr.md_image = (short *)craster;
	mpr.md_offset.x = 0;
	mpr.md_offset.y = 0;
	mpr.md_primary = 0;
	mpr.md_flags = 0;

	pr.pr_ops = &mem_ops;
	pr.pr_size.x = count;
	pr.pr_size.y = 1;
	pr.pr_depth = 1;
	pr.pr_data = (caddr_t)&mpr;

	CHECK_LOCK (sundata->ops, 10);
	pw_rop (sundata->clipped_pw, 
			sundata->x_fudge + where->x, sundata->y_fudge - where->y, 
			count, 1, PIX_SRC, (&pr), 0, 0);
}

local void draw_dc_pixel_array (mr, where, count, hoffset, hcount, rasters) 
	stack		Misc_Rendition			*mr; 
	stack		DC_Point				*where;
	stack		int						count, hoffset, hcount;
	stack		Color_Index				**rasters; {

	if (mr->h.display_context->physical.color_system == GRAY_SCALE)
		HD_Pixar_To_Bit_Rasters (mr, where, count, hoffset, hcount, rasters,
									  ADDR (draw_bit_raster));
	else
		HD_Pixar_To_Byte_Rasters (mr, where, count, hoffset, hcount, rasters,
									  ADDR (draw_raster));
}

#ifndef MES
special_events (event, window)
	Event				*event; 
	Sunview_Window		window; {

}
#endif

local void all_events (window, event, arg) 
	stack		Sunview_Window			window;
	stack		Sunview_Event			*event;
	stack		caddr_t					arg; {
	stack		Display_Context			*dc; 
	stack		SunData					*sundata;
	stack		int						this_button;

	dc = (Display_Context *) window_get (window, WIN_CLIENT_DATA);
	sundata = (SunData *) dc->data;
		
	if	(event_is_button (event))  {
		this_button =  1  <<  (event_id (event) - BUT_FIRST);
		if	(event_is_up (event))  {
				sundata->current_buttons &= ~this_button;
		}
		else {
				sundata->current_buttons |= this_button;
		}
		sundata->current_x = event_x (event);
		sundata->current_y = event_y (event);
		if (dc->options.special_events && sundata->current_buttons > 0) {
			stack		char			buf[64];		

			sprintf (buf, "event = %ld", event);
			HC_Queue_Special_Event ("sunview event", buf);
			special_events (event, window);
		}
	}
	else if	 (event_is_ascii (event) || event_id(event) >= KEY_LEFT(1)) {
		if (sundata->next_kbd > &sundata->kbd[MAX_KEYSTROKES]) {
			HI_Warning ("Flushing keyboard input buffer");
			sundata->first_kbd = sundata->next_kbd = &sundata->kbd[0];
		}
		sundata->next_kbd->ascii = event_id (event);
		sundata->next_kbd->special = 0;
		if(event_shift_is_down(event)) sundata->next_kbd->special |= KBD_SHIFT;
		if(event_ctrl_is_down(event)) sundata->next_kbd->special  |= KBD_CONTROL;
		if(event_meta_is_down(event)) sundata->next_kbd->special  |= KBD_META;
		++sundata->next_kbd;
	}
	else if	 (event_id (event) == LOC_STILL)  {
		sundata->current_x = event_x (event);
		sundata->current_y = event_y (event);
	}
	else if	 (event_id (event) == LOC_MOVE)	 {
		sundata->current_x = event_x (event);
		sundata->current_y = event_y (event);
	}
	else if	 (event_id (event) == LOC_WINEXIT)	{
		sundata->current_buttons = 0;
		sundata->current_x = 0;
		sundata->current_y = 0;
	}
}

local void resize(canvas, width, height) 
	stack		Canvas					canvas; 
	stack		int						width,
										height; {
	stack		Display_Context			*dc; 
	stack		SunData					*sundata;

	dc = (Display_Context *) window_get (canvas, WIN_CLIENT_DATA);
	sundata = (SunData *) dc->data;

	sundata->was_resized = true;
}

local void repaint(canvas, pw, rectlist) 
	stack		Canvas					canvas;
	stack		Pixwin					*pw;
	stack		Rectlist				*rectlist; {
	stack		Display_Context			*dc; 
	stack		SunData					*sundata;
	extern		void					HD_Standard_Draw_Tree();
	stack		char					tmp[200];

	dc = (Display_Context *) window_get (canvas, WIN_CLIENT_DATA);
	sundata = (SunData *) dc->data;
	
	sundata->needs_repainting = true;
}


/* this routine should only be called if dc->options.use_window_ID == 0 */
local Notify_value catch_quit (frame, status) 
	stack		Frame					frame;
	stack		Destroy_status			status; {
	stack		Display_Context			*dc; 
	stack		SunData					*sundata;
	stack		int						tmp;
	
	tmp = (int) window_get (frame, WIN_CLIENT_DATA);

	if (tmp != DONT_EXIT) {
		dc = (Display_Context *) tmp;
		sundata = (SunData *) dc->data;
	}

	if (status == DESTROY_CHECKING) {
		return notify_next_destroy_func (frame, status);
	}
	else if (status == DESTROY_CLEANUP) {
		notify_next_destroy_func (frame, status);
		if (tmp != DONT_EXIT) {
			(void) notify_stop();
			HI_Normal_Exit();
		}
	}
	else if (status == DESTROY_PROCESS_DEATH) {
		if (tmp != DONT_EXIT) {
			(void) notify_stop();
			HI_Normal_Exit();
		}
	}
}

local void fake_see_whats_hidden (dc) 
	stack		Display_Context *dc; {
	stack		SunData			*sundata = (SunData *) dc->data;

	if (!sundata->screen_locked) {
		pw_lock (sundata->clipped_pw, &sundata->windowinfo);   
		sundata->screen_locked = true;
	}

	HD_See_Whats_Hidden (dc);
}

local void fake_insert_in_hidden_tree (new) 
	stack		struct hidden			*new; {
	stack		Display_Context			*dc = new->rendition->h.display_context;
	stack		SunData					*sundata = (SunData *) dc->data;

	if (sundata->screen_locked) {
		pw_unlock (sundata->clipped_pw);
		sundata->screen_locked = false;
	}

	HD_Insert_In_Hidden_Tree (new);
}


local long load_font (dc, name, Aheight, vspace, widths)
	stack		Display_Context			*dc; 
	stack		char					*name; 
	stack		int						*Aheight,
										*vspace;
	stack		Character_Width			*widths; {
	stack		char					fullname[128];
	stack		Pixfont					*pf;
	stack		int						size;

	sprintf(fullname, "/usr/lib/fonts/fixedwidthfonts/%s", name);
	
	if (pf = pf_open(fullname)) {
		register		int		i;

		if (*Aheight == -1) {
			*Aheight = -pf->pf_char[65].pc_home.y;
		}
		if (*vspace == -1) {
			*vspace = pf->pf_defaultsize.y;
		}
		for (i = 0; i < 256; ++i) {
			widths[i] = pf->pf_char[i].pc_adv.x;
		}
		return ((long)pf);
	}
	else {
		return (0);
	}
}

local void unload_font (dc, identifier)
	stack		Display_Context			*dc;
	stack		long					identifier; {

	pf_close((Pixfont *) identifier);
}

local void find_all_fonts (dc)
	stack		Display_Context					*dc; {
	stack		DIR								*dir; 
	extern		DIR								*opendir(); 
	stack		struct direct					*entry;
	extern		struct direct					*readdir();
	stack		Pixfont							*pf;
	stack		char							fullname[128];
	stack		char							shortname[128];

	dir = opendir("/usr/lib/fonts/fixedwidthfonts");

	for (entry = readdir(dir); entry != NULL; entry = readdir(dir)) {

		/* make a zero-terminated string, not sure if it already is */
		HI_Copy_String_To_Max(entry->d_name, entry->d_namlen, shortname);
		shortname[entry->d_namlen] = '\0';

		sprintf(fullname, "/usr/lib/fonts/fixedwidthfonts/%s", shortname);
		if (pf = pf_open(fullname)) {
			stack		char			fname[32];
			stack		char			family[32];
			stack		char			type[32];
			stack		char			size[32];

			if (HC_Parse_String (entry->d_name, ".", 0, family) &&
				HC_Parse_String (entry->d_name, ".", 1, type)	&&
				HC_Parse_String (entry->d_name, ".", 2, size)) {
		
				sprintf(fname, "%s.%s", family, type);
				HD_Record_Font(dc, fname, shortname, -1, -1);
			}
			pf_close(pf);
		}
	}
	closedir (dir);

	/**
	 *	Now mark the Hoops Generic Font Families.  Use the SunView
	 *	font family names that match the general characteristic we
	 *	are looking for - "regular" fonts.
	 *
	 *	The Hoops Generic "Sans Serif", "Typewriter", and "Roman"
	 *	fonts are the "regular screen", "regular cour", and
	 *	"regular cmr", respectively.
	 *
	 *	The Hoops System Default font is the "regular screen" font
	 *	in size 14.	 If it is not available, try size 13.
	**/

	HD_Mark_Font_As_Generic(dc, "screen.r", GFN_SANS_SERIF);
	HD_Mark_Font_As_Generic(dc, "cour.r", GFN_TYPEWRITER);
	HD_Mark_Font_As_Generic(dc, "cmr.r", GFN_ROMAN);
	if (!HD_Mark_Font_As_System_Default(dc, "screen.r.14", -1)) {
		HD_Mark_Font_As_System_Default(dc, "screen.r.13", -1);
	}
}


local void draw_dc_text (xr, where, count, string)
	stack		Text_Rendition			*xr;
	stack		DC_Point				*where;
	stack		int						count; 
	stack		char					*string; {
	stack		Display_Context			*dc = xr->h.display_context;
	register	SunData					*sundata = (SunData *)dc->data;
	register	int						xi, yi;
	stack		int						fore, 
										back;

	if (dc->physical.number_of_colors == 2) {
		if (xr->color.gray_level > MAX_GRAY_LEVEL * 0.5)
			fore = 1;
		else
			fore = 0;
	}
	else		
		fore = INDEX (sundata->lower_colormap, xr->color.map_index, 
					  sundata->shift);

	if (xr->h.transform_rendition->h.incarnation != sundata->last_clipping) 
		new_clipping (xr->h.transform_rendition);
 
	/* adjust reference point for upside-down sun and clipping pixrect */
	xi = sundata->x_fudge + where->x;
	yi = sundata->y_fudge - where->y;

	pw_ttext (sundata->clipped_pw, xi, yi, PIX_SRC | PIX_COLOR (fore), 
				xr->font->identifier, string);

	sundata->last_rendition = xr->h.incarnation;

}

local void get_actions (at) 
	stack		Action_Table			*at; {

	at->start_device					= ADDR(start_device);
	at->stop_device						= ADDR(stop_device);
	at->get_physical_info				= ADDR(get_physical_info);
	at->get_current_info				= ADDR(get_current_info);
	at->init_update						= ADDR(init_update);
	at->finish_update					= ADDR(finish_update);
	at->init_picture					= ADDR(init_picture);
	at->finish_picture					= ADDR(finish_picture);
	at->draw_dc_rectangle				= ADDR(draw_dc_rectangle);
	at->draw_dc_polygon					= ADDR (draw_dc_polygon);
	at->draw_dc_overlay_face			= ADDR(draw_dc_overlay_face);
	at->draw_dc_blended_face			= ADDR(draw_dc_blended_face);
	at->draw_dc_face					= ADDR(draw_dc_face);
	at->draw_dc_polyline				= ADDR(draw_dc_polyline);
	at->draw_dc_overlay_polyline		= ADDR (draw_dc_overlay_polyline);
	at->draw_dc_pixel_array				= ADDR(draw_dc_pixel_array);
	at->draw_dc_text					= ADDR(draw_dc_text);
	at->get_outer_window				= ADDR(get_outer_window);
	at->resize_outer_window				= ADDR(resize_outer_window);
	at->free_outer_window				= ADDR(free_outer_window);
	at->set_device_color_map			= ADDR(set_device_color_map);
	at->request_keyboard				= ADDR(request_keyboard);
	at->request_location				= ADDR(request_location);
	at->insert_in_hidden_tree			= ADDR(fake_insert_in_hidden_tree);
	at->see_whats_hidden				= ADDR(fake_see_whats_hidden);
	at->find_all_fonts					= ADDR(find_all_fonts);
	at->load_font						= ADDR(load_font);
	at->unload_font						= ADDR(unload_font);
}

define_patterns (dc) 
	stack		Display_Context			*dc; {
	register	SunData					*sundata = (SunData *) dc->data; 
	register	int						i, j; 

	if (sundata->total_planes == 1) {
		HD_Get_Bit_Patterns (dc, false);
	}
	else {
		HD_Get_Bit_Patterns_8 (dc, false);

		for (i = 0; i < TOTAL_BIT_PATTERNS; ++i) {
			register	unsigned char	*in, *out;

			out = &sundata->fill_patterns[i][0]; 
			in = (unsigned char *)dc->bit_pattern[i];	

			for (j = 0; j < 8; ++j ) {
				if (*in & 0x0080) *out++ = 1; else *out++ = 0;
				if (*in & 0x0040) *out++ = 1; else *out++ = 0;
				if (*in & 0x0020) *out++ = 1; else *out++ = 0;
				if (*in & 0x0010) *out++ = 1; else *out++ = 0;
				if (*in & 0x0008) *out++ = 1; else *out++ = 0;
				if (*in & 0x0004) *out++ = 1; else *out++ = 0;
				if (*in & 0x0002) *out++ = 1; else *out++ = 0;
				if (*in & 0x0001) *out++ = 1; else *out++ = 0;
				++in;
			}
		}
	}
}

local int roundoff (i) 
	register	int		i; {

	if (i >= 32) 
		if (i >= 128)  
			if (i >= 256)  
				return 256;
			else
				return 128; 
		else 
			if (i >= 64)  
				return 64;
			else
				return 32;
	else 
		if (i >= 8)	 
			if (i >= 16)  
				return 16;
			else 
				return 8;
		else 
			if (i >= 4)	 
				return 4;
			else 
				return 2;
}


local boolean is_suntools_running() {
	extern		char			*getenv();

	return (getenv ("WINDOW_PARENT") != null);
}
	
local void get_screen_dimensions (dc) 
	register	Display_Context			*dc; {
	stack		Frame					frame;
	stack		struct screen			screen;
	stack		int						fd;
	stack		Canvas					canvas;
	stack		Pixwin					*pw;
	register	SunData					*sundata = (SunData *) dc->data; 

	frame  = window_create (
						0, FRAME, 
						WIN_SHOW, FALSE,
						FRAME_NO_CONFIRM, TRUE,
						0);

	if (frame == null) {
		dc->physical.number_of_pixels.x = 1152;
		dc->physical.number_of_pixels.y = 900;
		dc->physical.size.x = 11.52 * 2.54;
		dc->physical.size.y =  9.0 * 2.54;						 
		HI_Warning ("Could not query actual Sunview screen dimensions");
		return;
	}

	fd = (int) window_get (frame, WIN_FD);
	win_screenget (fd, &screen);
	dc->physical.number_of_pixels.x = screen.scr_rect.r_width;
	dc->physical.number_of_pixels.y = screen.scr_rect.r_height;

	dc->physical.size.x = dc->physical.number_of_pixels.x * 0.0254;
	dc->physical.size.y = dc->physical.number_of_pixels.y * 0.0254;

	canvas = window_create (frame, CANVAS, 0);
	pw = canvas_pixwin (canvas);
	dc->physical.nominal_color_resolution = COLOR_RESOLUTION;
	
	if (pw->pw_pixrect->pr_depth == 1) {
		dc->physical.color_system = GRAY_SCALE;
		dc->physical.number_of_colors = 2;
		dc->physical.nominal_color_resolution = MAX_GRAY_LEVEL;
	}
	else if (pw->pw_pixrect->pr_depth == 8) {
		dc->physical.color_system = MAPPED_RGB;
		dc->physical.number_of_colors = 256;
		dc->physical.nominal_color_resolution = 256;
	}
	else {
		HI_Error ("Hoops must be run on a 1 bit or 8 bit machine");
	}

	window_destroy (canvas);
	window_destroy (frame);
}

local boolean create_frame_and_canvas (dc, title)
	stack		Display_Context *dc;
	stack		char			*title; {
	register	SunData			*sundata = (SunData *) dc->data;
	stack		int				window_type;
 
	if (dc->options.use_window_ID != 0) {
		stack			int		left,
								right,
								bottom,
								top;
		stack			int		canvas_margin;
		stack			Rect	*open_frame;
 
		window_set (sundata->frame,
				WIN_CLIENT_DATA, (char *) dc,
				0);
 
 
		open_frame = (Rect *) window_get (sundata->frame, FRAME_OPEN_RECT);
		sundata->created_windowinfo.r_left = open_frame->r_left;
		sundata->created_windowinfo.r_top = open_frame->r_top;
		sundata->created_windowinfo.r_width = open_frame->r_width;
		sundata->created_windowinfo.r_height = open_frame->r_height;
 
	}
	else {
		sundata->created_windowinfo.r_width =
			dc->outer.extent.right - dc->outer.extent.left	+ 1 +
			Sunview_BORDER * 2;
		sundata->created_windowinfo.r_height =
			dc->outer.extent.top - dc->outer.extent.bottom + 1 +
			Sunview_BORDER + Sunview_CONTROL_AREA;
		sundata->created_windowinfo.r_left =
			dc->outer.extent.left - Sunview_BORDER;
		sundata->created_windowinfo.r_top =
			dc->physical.number_of_pixels.y - dc->outer.extent.top -
			Sunview_CONTROL_AREA;
	  
		if (!sundata->marked_for_delete) { 
			if ((sundata->frame	 = window_create (0, FRAME,
					WIN_SHOW, TRUE,
					FRAME_OPEN_RECT, &sundata->created_windowinfo,
					FRAME_LABEL, title,
					WIN_CLIENT_DATA, (char *) dc,
					FRAME_NO_CONFIRM, TRUE,
					0)) == 0) {

				HI_Error ("Could not create SunView frame");
				return false;
			}
 
			if ((sundata->canvas =
				window_create(sundata->frame, CANVAS, 0)) == 0) {
				HI_Error ("Could not create SunView canvas");
				return false;
			}
		
			notify_interpose_destroy_func(sundata->frame, catch_quit);
		}
		else {
			window_set (sundata->frame,
					FRAME_OPEN_RECT, &sundata->created_windowinfo,
					FRAME_LABEL, title,
					WIN_CLIENT_DATA, (char *) dc,
					FRAME_NO_CONFIRM, TRUE,
					0);
		} 
	 }
	   
	window_set (sundata->canvas,
				WIN_CLIENT_DATA, (char *) dc,
				WIN_EVENT_PROC, all_events,
				0);
	window_set (sundata->canvas,
				CANVAS_AUTO_EXPAND, TRUE,
				CANVAS_AUTO_SHRINK, TRUE,
				CANVAS_RETAINED, FALSE,
				CANVAS_FIXED_IMAGE, FALSE,
				CANVAS_REPAINT_PROC, repaint,
				CANVAS_RESIZE_PROC, resize,
				CANVAS_AUTO_CLEAR, FALSE,
				0);

	if (!dc->options.disable_input) {
			window_set (sundata->canvas,
				WIN_CONSUME_PICK_EVENT, WIN_MOUSE_BUTTONS,
				WIN_CONSUME_PICK_EVENT, WIN_UP_EVENTS,
				WIN_CONSUME_PICK_EVENT, LOC_MOVE,
				WIN_CONSUME_PICK_EVENT, LOC_STILL,
				WIN_CONSUME_KBD_EVENT, WIN_ASCII_EVENTS,
				WIN_CONSUME_KBD_EVENT, WIN_RIGHT_KEYS,
				WIN_CONSUME_KBD_EVENT, WIN_LEFT_KEYS,
				WIN_CONSUME_KBD_EVENT, WIN_TOP_KEYS,
				0);
	}
		
	sundata->pw = canvas_pixwin (sundata->canvas);

	return true;
}

#endif
