/* Copyright 1997 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/***************************************************/
/* File   : FromROSLib.c                           */
/*                                                 */
/* Purpose: The original Customer browser sources */
/*          used RISC_OSLib heavily and some of    */
/*          the functions used are handy to have   */
/*          around. This source file contains      */
/*          a variety of functions from RISC_OSLib */
/*          with modifications where necessary     */
/*          both to fit into the browser more      */
/*          naturally, and to function better      */
/*          under C release 5.                     */
/*                                                 */
/* Authors: Various for original RISC_OSLib source */
/*          This source adapted by A.D.Hodgkinson  */
/*                                                 */
/* History: 06-Dec-96: Created.                    */
/***************************************************/

#include <stdlib.h>
#include <stdarg.h>

#include "swis.h"

#include "wimp.h"

#include "FromROSLib.h"

/* Statics */

/* Array of length of sequence for vdu codes, */
/* used by the VDU call-based functions.      */

static char Qlen[32] =
{

  1,   /* VDU 0                          */
  2,   /* Next character to printer only */
  1,   /* Printer on                     */
  1,   /* Printer off                    */
  1,   /* Print at text cursor           */
  1,   /* Print at graphics cursor       */
  1,   /* Enable VDU driver              */
  1,   /* Beep                           */
  1,   /* Backspace                      */
  1,   /* Forward space (horizontal tab) */
  1,   /* Line feed                      */
  1,   /* Up a line                      */
  1,   /* Clear (text) screen            */
  1,   /* Carriage return                */
  1,   /* Page mode on                   */
  1,   /* Page mode off                  */
  1,   /* Clear graphics window          */
  2,   /* Define text colour             */
  3,   /* Define graphics colour         */
  6,   /* Define logical colour          */
  1,   /* Restore default palette        */
  1,   /* Disable VDU drivers            */
  2,   /* Select screen mode             */
  10,  /* VDU 23,..                      */
  9,   /* Set graphics window            */
  6,   /* PLOT ...                       */
  1,   /* Restore default windows        */
  1,   /* ESCAPE char - no effect        */
  5,   /* Define text window             */
  5,   /* Define graphics origin         */
  1,   /* Home cursor                    */
  3    /* Tab cursor                     */

};       /* ...and all the rest are 1. */

/* Cached eigen values */

static int wimpt_xeig = 2;
static int wimpt_yeig = 2;

/*************************************************/
/* bbc_modevar()                                 */
/*                                               */
/* Returns the value of a mode variable, or -1   */
/* if there was some error.                      */
/*                                               */
/* Parameters: A mode number or -1 for current;  */
/*                                               */
/*             A mode variable (for the SWI call */
/*             OS_ReadModeVariable).             */
/*                                               */
/* Returns:    The value of the variable, or -1  */
/*             if an error occurs.               */
/*************************************************/

int bbc_modevar(int mode, int varno)
{
  int result;

  if (_swix(OS_ReadModeVariable,
            _INR(0,1) | _OUT (2),
            mode,
            varno,
            &result)) return -1;

  return result;
}

/*************************************************/
/* bbc_vduvar()                                  */
/*                                               */
/* Returns the value of a VDU variable, or -1 to */
/* flag an error - therefore, if the variable    */
/* may hold -1 as a valid value, you need to use */
/* bbc_vduvars() instead.                        */
/*                                               */
/* Parameters: A VDU variable number for the     */
/*             SWI call OS_ReadVduVariables.     */
/*                                               */
/* Returns:    The value of the variable, or -1  */
/*             if an error occurs.               */
/*************************************************/

int bbc_vduvar(int varno)
{
   int vars[2];
   int result;

   vars[0] = varno;
   vars[1] = -1; /* terminator */

   return _swix(OS_ReadVduVariables,
                _INR(0,1),
                vars,
                &result)
                != NULL ? -1 : result; /* relies on -1 never being valid */
}

/*************************************************/
/* bbc_vduvars()                                 */
/*                                               */
/* Returns values of any number of VDU variables */
/*                                               */
/* Parameters: A pointer to a block holding a    */
/*             list of variable numbers,         */
/*             termianted by -1;                 */
/*                                               */
/*             A pointer to a block into which   */
/*             the corresponding variable values */
/*             will be placed. This may be the   */
/*             same block as the one holding the */
/*             variable numbers.                 */
/*                                               */
/* Returns:    The block holding the variable    */
/*             numbers is updated with the       */
/*             variable values, assuming the     */
/*             call doesn't return an error.     */
/*************************************************/

_kernel_oserror * bbc_vduvars(int * vars, int * values)
{
   return _swix(OS_ReadVduVariables,
                _INR(0,1),

                (int) vars,
                (int) values);
}

/*************************************************/
/* bbc_vduq()                                    */
/*                                               */
/* Output multiple characters. Runs through an   */
/* assembler routine bbc_vdu for speed.          */
/*                                               */
/* Parameters: A control character - the number  */
/*             of further characters to expect   */
/*             is defined in the static 'Qlen',  */
/*             defined at the top of this file,  */
/*             and depends on the char given.    */
/*************************************************/

_kernel_oserror * bbc_vduq(int c,...)
{
   _kernel_oserror * e;
   va_list           ap;
   int               n;

   e = bbc_vdu(c); /* See FromROSLib.s */

   if ((c >= ' ') || e) return(e);

   va_start(ap, c);
   n = Qlen[c];

   while ((--n) && (!e)) e = bbc_vdu(va_arg(ap,int));

   va_end(ap);

   return(e);
}

/*************************************************/
/* bbc_clg()                                     */
/*                                               */
/* Clears the current graphics window.           */
/*************************************************/

_kernel_oserror * bbc_clg(void)
{
  return (bbc_vdu(BBC_ClearGraph));
}

/*************************************************/
/* bbc_gwindow()                                 */
/*                                               */
/* Defines a graphics window.                    */
/*                                               */
/* Parameters: X coordinate of bottom left hand  */
/*             corner of graphics window in OS   */
/*             units, inclusive;                 */
/*                                               */
/*             Y coordinate for the same;        */
/*                                               */
/*             X coordinate of top right hand    */
/*             corner of graphics window in OS   */
/*             units, exclusive;                 */
/*                                               */
/*             Y coordinate for the same.        */
/*************************************************/

_kernel_oserror * bbc_gwindow(int xmin, int ymin, int xmax, int ymax)
{
  _kernel_oserror * e = bbc_vdu(BBC_DefGraphWindow);

  if (!e) e = bbc_vduw(xmin);
  if (!e) e = bbc_vduw(ymin);
  if (!e) e = bbc_vduw(xmax);
  if (!e) e = bbc_vduw(ymax);

  return e;
}

/*************************************************/
/* bbc_origin()                                  */
/*                                               */
/* Sets the graphics plotting origin.            */
/*                                               */
/* Parameters: X coordinate of origin, in OS     */
/*             units;                            */
/*                                               */
/*             Y coordinate for the same.        */
/*************************************************/

_kernel_oserror * bbc_origin(int x, int y)
{
   _kernel_oserror * e = bbc_vdu(BBC_DefGraphOrigin);

   if (!e) e = bbc_vduw(x);
   if (!e) e = bbc_vduw(y);

   return e;
}

/*************************************************/
/* bbc_gcol()                                    */
/*                                               */
/* Sets a graphics colour and plotting action.   */
/*                                               */
/* Graphics plotting actions:                    */
/*                                               */
/* P is pixel on screen.                         */
/* N is new colour.                              */
/*                                               */
/* Action  Behaviour                             */
/*                                               */
/* 0       P = N                                 */
/* 1       P |= N (OR)                           */
/* 2       P &= N (AND)                          */
/* 3       P = P ^ N (XOR)                       */
/* 4       P = !P (NOT)                          */
/* 5       P = P (no change to screen colour)    */
/* 6       P &= (!N) (AND NOT)                   */
/* 7       P |= (!N) (OR NOT)                    */
/* 8 -15   As 0-7 but background is transparent  */
/* 16-31   Pattern 1, with action 0 - 15         */
/* 32-47   Pattern 2, with action 0 - 15         */
/* 48-63   Pattern 3, with action 0 - 15         */
/* 64-79   Pattern 4, with action 0 - 15         */
/* 80-95   Large pattern, with action 0 - 15     */
/*                                               */
/* Parameters: Plotting action (see above);      */
/*                                               */
/*             Graphics colour (0-127 for the    */
/*             foreground, else background is    */
/*             set to colour - 128). The colour  */
/*             used is ANDed with the maximum    */
/*             number of colours for the screen  */
/*             mode (1,3, 15 or 63 for 2, 4, 16  */
/*             or >=256 colour modes).           */
/*************************************************/

_kernel_oserror * bbc_gcol(int a, int b)
{
   _kernel_oserror * e = bbc_vdu(BBC_DefGraphColour);

   if (!e) e = bbc_vdu(a);
   if (!e) e = bbc_vdu(b);

   return e;
}

/*************************************************/
/* bbc_plot()                                    */
/*                                               */
/* Plot something on the screen with OS_Plot.    */
/* See PRM volume 1 page 607 for full details.   */
/*                                               */
/* Parameters: Plot number;                      */
/*                                               */
/*             X coordinate;                     */
/*                                               */
/*             Y coordinate.                     */
/*************************************************/

_kernel_oserror * bbc_plot(int n, int x, int y)
{
  return _swix(OS_Plot,
               _INR(0,2),
               n,
               x,
               y);
}

/*************************************************/
/* bbc_move()                                    */
/*                                               */
/* Move the graphics cursor to absolute coords.  */
/*                                               */
/* Parameters: X coordinate to move to, in OS    */
/*             units;                            */
/*                                               */
/*             Y coordinate for the same.        */
/*************************************************/

_kernel_oserror * bbc_move(int x, int y)
{
   return bbc_plot(BBC_MoveCursorAbs, x, y);
}

/*************************************************/
/* bbc_moveby()                                  */
/*                                               */
/* Move the graphics cursor to absolute coords.  */
/*                                               */
/* Parameters: X offset to move by, in OS units; */
/*                                               */
/*             Y coordinate for the same.        */
/*************************************************/

_kernel_oserror * bbc_moveby(int x, int y)
{
   return bbc_plot(BBC_MoveCursorRel, x, y);
}

/*************************************************/
/* bbc_draw()                                    */
/*                                               */
/* Draws a solid line including both end points  */
/* to absolute coordinates, starting the line at */
/* the current graphics cursor position.         */
/*                                               */
/* Parameters: X coordinate to draw to, in OS    */
/*             units;                            */
/*                                               */
/*             Y coordinate for the same.        */
/*************************************************/

_kernel_oserror * bbc_draw(int x, int y)
{
   return bbc_plot(BBC_SolidBoth + BBC_DrawAbsFore, x, y);
}

/*************************************************/
/* bbc_drawby()                                  */
/*                                               */
/* Draws a solid line including both end points  */
/* to relative coordinates, starting the line at */
/* the current graphics cursor position.         */
/*                                               */
/* Parameters: X offset to draw by, in OS units; */
/*                                               */
/*             Y coordinate for the same.        */
/*************************************************/

_kernel_oserror * bbc_drawby(int x, int y)
{
   return bbc_plot(BBC_SolidBoth + BBC_DrawRelFore, x, y);
}

/*************************************************/
/* bbc_rectangle()                               */
/*                                               */
/* Plots a rectangle outline.                    */
/*                                               */
/* Parameters: X coordinate of bottom left hand  */
/*             corner, in OS units;              */
/*                                               */
/*             Y coordinate for the same;        */
/*                                               */
/*             Rectangle width plus one, in OS   */
/*             units - this is added to the X    */
/*             value to produce an *exclusive*   */
/*             top right hand corner coordinate, */
/*             hence for an 8 OS unit wide       */
/*             rectangle, say, you'd pass in 7;  */
/*                                               */
/*             Similarly, the rectangle height.  */
/*************************************************/

_kernel_oserror * bbc_rectangle(int x, int y, int w, int h)
{
   _kernel_oserror * e = bbc_move(x,y);

   if (!e) e = bbc_plot(BBC_SolidExFinal + BBC_DrawRelFore,  0,  h);
   if (!e) e = bbc_plot(BBC_SolidExFinal + BBC_DrawRelFore,  w,  0);
   if (!e) e = bbc_plot(BBC_SolidExFinal + BBC_DrawRelFore,  0, -h);
   if (!e) e = bbc_plot(BBC_SolidExFinal + BBC_DrawRelFore, -w,  0);

   return e;
}

/*************************************************/
/* bbc_rectanglefill()                           */
/*                                               */
/* Plots a filled rectangle.                     */
/*                                               */
/* Parameters: As bbc_rectangle.                 */
/*************************************************/

_kernel_oserror * bbc_rectanglefill(int x, int y, int w, int h)
{
   _kernel_oserror * e = bbc_move(x,y);

   if (!e) e = bbc_plot(BBC_RectangleFill + BBC_DrawRelFore, w, h);

   return e;
}

/*************************************************/
/* bbc_circle()                                  */
/*                                               */
/* Plots a circle outline.                       */
/*                                               */
/* Parameters: X coordinate of midpoint, in OS   */
/*             units;                            */
/*                                               */
/*             Y coordinate for the same;        */
/*                                               */
/*             Radius, in OS units.              */
/*************************************************/

_kernel_oserror * bbc_circle(int x, int y, int r)
{
   _kernel_oserror * e = bbc_move(x,y);

   if (!e) e = bbc_plot(BBC_Circle + BBC_DrawAbsFore, x + r, y);

   return e;
}

/*************************************************/
/* bbc_circlefill()                              */
/*                                               */
/* Plots a filled circle.                        */
/*                                               */
/* Parameters: As bbc_circle.                    */
/*************************************************/

_kernel_oserror * bbc_circlefill(int x, int y, int r)
{
   _kernel_oserror * e = bbc_move(x,y);

   if (!e) e = bbc_plot(BBC_CircleFill + BBC_DrawAbsFore, x + r, y);

   return e;
}

/*************************************************/
/* bbc_trianglefill()                            */
/*                                               */
/* Plots a filled triangle.                      */
/*                                               */
/* Parameters: X coordinate of one corner, in    */
/*             OS units;                         */
/*                                               */
/*             Y coordinate of one corner, in    */
/*             OS units;                         */
/*                                               */
/*             X coordinate of another corner,   */
/*             in OS units;                      */
/*                                               */
/*             Y coordinate of another corner,   */
/*             in OS units;                      */
/*                                               */
/*             X coordinate of the last corner,  */
/*             in OS units;                      */
/*                                               */
/*             Y coordinate of the last corner,  */
/*             in OS units.                      */
/*************************************************/

_kernel_oserror * bbc_trianglefill(int x1, int y1, int x2, int y2, int x3, int y3)
{
   _kernel_oserror * e = bbc_move(x1, y1);

   if (!e) e = bbc_move(x2, y2);
   if (!e) e = bbc_plot(BBC_TriangleFill + BBC_DrawAbsFore, x3, y3);

   return e;
}

/*************************************************/
/* bbc_fill()                                    */
/*                                               */
/* Flood-fill an area from absolute              */
/*                                               */
/* Parameters: X coordinate to start fill at, in */
/*             OS units;                         */
/*                                               */
/*             Y coordinate for the same.        */
/*************************************************/

_kernel_oserror * bbc_fill(int x, int y)
{
   return bbc_plot(BBC_FloodToBack + BBC_DrawAbsFore, x, y);
}

/*************************************************/
/* wimpt_read()                                  */
/*                                               */
/* Finds out the current X and Y eigen values    */
/* (desktop scaling). Screen resolution in dots  */
/* per inch is thus 180 / eigen value.           */
/*                                               */
/* This function is typically called on receipt  */
/* of a ModeChange message.                      */
/*************************************************/

void wimpt_read(void)
{
  wimpt_xeig = 1 << bbc_vduvar(BBC_XEigFactor);
  wimpt_yeig = 1 << bbc_vduvar(BBC_YEigFactor);
}

/*************************************************/
/* wimpt_dx()                                    */
/*                                               */
/* Returns the current X eigen value (Desktop    */
/* scaling). Screen X resolution in dots per     */
/* inch is thus 180/XEig.                        */
/*************************************************/

int wimpt_dx(void)
{
  return wimpt_xeig;
}

/*************************************************/
/* wimpt_dy()                                    */
/*                                               */
/* Returns the current Y eigen value (Desktop    */
/* scaling). Screen Y resolution in dots per     */
/* inch is thus 180/YEig.                        */
/*************************************************/

int wimpt_dy(void)
{
  return wimpt_yeig;
}

/*************************************************/
/* coords_x_toscreen()                           */
/*                                               */
/* Converts a work area X coordinate (0,0 is at  */
/* the top left of the window) to a screen       */
/* coordinate (0,0 is at the bottom left of the  */
/* screen).                                      */
/*                                               */
/* Parameters: The work area X coordinate;       */
/*                                               */
/*             Pointer to WimpRedrawWindowBlock  */
/*             holding details about the window  */
/*             that the X coordinate lies in.    */
/*                                               */
/* Returns:    The given value converted to a    */
/*             screen coordinate.                */
/*************************************************/

int coords_x_toscreen(int x, WimpRedrawWindowBlock * r)
{
  return (x - r->xscroll + r->visible_area.xmin);
}

/*************************************************/
/* coords_y_toscreen()                           */
/*                                               */
/* Converts a work area Y coordinate to a screen */
/* coordinate.                                   */
/*                                               */
/* Parameters: The work area Y coordinate;       */
/*                                               */
/*             Pointer to WimpRedrawWindowBlock  */
/*             holding details about the window  */
/*             that the Y coordinate lies in.    */
/*                                               */
/* Returns:    The given value converted to a    */
/*             screen coordinate.                */
/*************************************************/

int coords_y_toscreen(int y, WimpRedrawWindowBlock * r)
{
  return (y - r->yscroll + r->visible_area.ymax);
}

/*************************************************/
/* coords_box_toscreen()                         */
/*                                               */
/* Converts the contents of a BBox representing  */
/* a rectangle in the work area of a window to   */
/* screen coordinates.                           */
/*                                               */
/* Parameters: Pointer to the BBox;              */
/*                                               */
/*             Pointer to WimpRedrawWindowBlock  */
/*             holding details about the window  */
/*             that the BBox retangle lies in.   */
/*                                               */
/* Returns:    The contents of the BBox are      */
/*             updated to hold screen            */
/*             coordinates.                      */
/*************************************************/

void coords_box_toscreen(BBox * b, WimpRedrawWindowBlock * r)
{
  b->xmax = coords_x_toscreen(b->xmax, r);
  b->xmin = coords_x_toscreen(b->xmin, r);
  b->ymin = coords_y_toscreen(b->ymin, r);
  b->ymax = coords_y_toscreen(b->ymax, r);
}

/*************************************************/
/* coords_x_toworkarea()                         */
/*                                               */
/* Converts a screen X coordinate (0,0 is at the */
/* bottom left of the screen) to a work area     */
/* coordinate (0,0 is at the top left of the     */
/* window).                                      */
/*                                               */
/* Parameters: The screen X coordiante;          */
/*                                               */
/*             Pointer to WimpRedrawWindowBlock  */
/*             holding details about the window  */
/*             that the coordinate is to be      */
/*             transformed to lie in.            */
/*                                               */
/* Returns:    The given value converted to      */
/*             a work area coordinate.           */
/*************************************************/

int coords_x_toworkarea(int x, WimpRedrawWindowBlock * r)
{
  return (x + r->xscroll - r->visible_area.xmin);
}

/*************************************************/
/* coords_y_toworkarea()                         */
/*                                               */
/* Converts a screen Y coordinate to a work area */
/* coordinate.                                   */
/*                                               */
/* Parameters: The screen Y coordiante;          */
/*                                               */
/*             Pointer to WimpRedrawWindowBlock  */
/*             holding details about the window  */
/*             that the coordinate is to be      */
/*             transformed to lie in.            */
/*                                               */
/* Returns:    The given value converted to      */
/*             a work area coordinate.           */
/*************************************************/

int coords_y_toworkarea(int y, WimpRedrawWindowBlock * r)
{
  return (y + r->yscroll - r->visible_area.ymax);
}

/*************************************************/
/* coords_box_toworkarea()                       */
/*                                               */
/* Converts the contents of a BBox representing  */
/* a rectangle in screen coords to hold work     */
/* area coordinates.                             */
/*                                               */
/* Parameters: Pointer to the BBox;              */
/*                                               */
/*             Pointer to WimpRedrawWindowBlock  */
/*             holding details about the window  */
/*             that the BBox coordinates are to  */
/*             be transformed to lie in.         */
/*                                               */
/* Returns:    The contents of the BBox are      */
/*             updated to hold work area         */
/*             coordinates.                      */
/*************************************************/

void coords_box_toworkarea(BBox * b, WimpRedrawWindowBlock * r)
{
  b->xmax = coords_x_toworkarea(b->xmax, r);
  b->xmin = coords_x_toworkarea(b->xmin, r);
  b->ymin = coords_y_toworkarea(b->ymin, r);
  b->ymax = coords_y_toworkarea(b->ymax, r);
}