/* 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   : Mouse.c                                */
/*                                                 */
/* Purpose: Mouse pointer related functions for    */
/*          the browser.                           */
/*                                                 */
/* Author : A.D.Hodgkinson                         */
/*                                                 */
/* History: 14-Mar-97: Created.                    */
/***************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "swis.h"
#include "kernel.h"

#include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */

#include "wimp.h"
#include "wimplib.h"
#include "event.h"

#include "toolbox.h"

#include "svcprint.h"
#include "Global.h"
#include "Utils.h"

#include "Handlers.h"

#include "Mouse.h"

/* Locals */

static int mouse_watch = 0;

static int mouse_x;
static int mouse_y;
static int mouse_time;
static int mouse_on    = 1;
static int mouse_shape = 1;
static int mouse_used  = 0;

/*************************************************/
/* mouse_watch_pointer_control()                 */
/*                                               */
/* Controls watching movement of the mouse       */
/* pointer; if turned on, when the pointer moves */
/* the function will ensure that the pointer is  */
/* also visible.                                 */
/*                                               */
/* Parameters: 1 to turn watching on, 0 to turn  */
/*             it off.                           */
/*************************************************/

void mouse_watch_pointer_control(int on)
{
  if (on)
  {
    if (!mouse_watch)
    {
      mouse_x = mouse_y = mouse_time = -1;
      register_null_claimant(Wimp_ENull, mouse_watch_pointer, NULL);
      mouse_watch = 1;
    }
  }
  else
  {
    if (mouse_watch)
    {
      deregister_null_claimant(Wimp_ENull, mouse_watch_pointer, NULL);
      mouse_watch = 0;
    }
  }
}

/*************************************************/
/* mouse_watch_pointer()                         */
/*                                               */
/* Watches to see if the mouse pointer moves,    */
/* and if so, ensures the pointer is visible.    */
/*                                               */
/* Parameters are as standard for a Wimp event   */
/* handler.                                      */
/*************************************************/

int mouse_watch_pointer(int eventcode, WimpPollBlock * block, IdBlock * idb, void * handle)
{
  WimpGetPointerInfoBlock i;

  if (!wimp_get_pointer_info(&i))
  {
    if (mouse_x    < 0) mouse_x = i.x;
    if (mouse_y    < 0) mouse_y = i.y;
    if (mouse_time < 0) _swix(OS_ReadMonotonicTime, _OUT(0), &mouse_time);

    if (mouse_x != i.x || mouse_y != i.y)
    {
      if (!mouse_on) mouse_pointer_on();

      mouse_x = mouse_y = mouse_time = -1;

      mouse_used = 1;
    }
    else if (mouse_on)
    {
      int t;

      _swix(OS_ReadMonotonicTime, _OUT(0), &t);

      if (t - mouse_time > 500) mouse_pointer_off();
    }
  }

  return 0;
}

/*************************************************/
/* mouse_pointer_on()                            */
/*                                               */
/* Turns the mouse pointer on immediately.       */
/*************************************************/

void mouse_pointer_on(void)
{
  if (!mouse_on)
  {
    mouse_x = mouse_y = mouse_time = -1;
    mouse_on = 1;
    mouse_set_pointer_shape(mouse_shape);
  }
}

/*************************************************/
/* mouse_pointer_off()                           */
/*                                               */
/* Turns the mouse pointer off immediately.      */
/*************************************************/

void mouse_pointer_off(void)
{
  if (mouse_on && !mouse_used)
  {
    mouse_x = mouse_y = mouse_time = -1;
    mouse_set_pointer_shape(Mouse_Shape_Off);
    mouse_on = 0;
  }
}

/*************************************************/
/* mouse_force_unused()                          */
/*                                               */
/* Resets the internal mouse_used flag back to   */
/* zero, so that the mouse pointer may be        */
/* turned off (otherwise the user has moved the  */
/* pointer and it shouldn't be switched off).    */
/*************************************************/

void mouse_force_unused(void)
{
  mouse_used = 0;
}

/*************************************************/
/* mouse_set_pointer_shape()                     */
/*                                               */
/* Sets the shape of the pointer; shape 0 turns  */
/* it off. If mouse_on is set to zero, this will */
/* *not* override that setting.                  */
/*                                               */
/* Parameters: Pointer shape; see Mouse.h for    */
/*             definitions of shapes.            */
/*************************************************/

void mouse_set_pointer_shape(int shape)
{
  if (shape) mouse_shape = shape;

  /* If the mouse is turned off, don't turn it back on... */

  if (shape && !mouse_on) return;

  /* Otherwise change to the required shape */

  switch (shape)
  {
    case Mouse_Shape_Off:    _swix(OS_CLI,
                                   _IN(0),

                                   "Pointer 0");
    break;

    default: /* Catchall - use normal pointer, so no 'break' */

    case Mouse_Shape_Normal:    _swix(OS_CLI,
                                      _IN(0),

                                      "Pointer 1");
    break;

    case Mouse_Shape_Link:      _swix(OS_SpriteOp,
                                      _INR(0,7),

                                      292,
                                      sprite_block,
                                      "ptr_link",
                                      2,
                                      controls.ptrlnkactvx,
                                      controls.ptrlnkactvy,
                                      0,
                                      0);
    break;

    case Mouse_Shape_Map:       _swix(OS_SpriteOp,
                                      _INR(0,7),

                                      292,
                                      sprite_block,
                                      "ptr_map",
                                      2,
                                      controls.ptrmapactvx,
                                      controls.ptrmapactvy,
                                      0,
                                      0);
    break;

    case Mouse_Shape_UD:        _swix(OS_SpriteOp,
                                      _INR(0,7),

                                      292,
                                      sprite_block,
                                      "ptr_ud",
                                      2,
                                      controls.ptrudactvx,
                                      controls.ptrudactvy,
                                      0,
                                      0);
    break;

    case Mouse_Shape_LR:        _swix(OS_SpriteOp,
                                      _INR(0,7),

                                      292,
                                      sprite_block,
                                      "ptr_lr",
                                      2,
                                      controls.ptrlractvx,
                                      controls.ptrlractvy,
                                      0,
                                      0);
    break;

    case Mouse_Shape_UDLR:      _swix(OS_SpriteOp,
                                      _INR(0,7),

                                      292,
                                      sprite_block,
                                      "ptr_udlr",
                                      2,
                                      controls.ptrudlractvx,
                                      controls.ptrudlractvy,
                                      0,
                                      0);
    break;

    case Mouse_Shape_NoResize:  _swix(OS_SpriteOp,
                                      _INR(0,7),

                                      292,
                                      sprite_block,
                                      "ptr_noresize",
                                      2,
                                      controls.ptrnoractvx,
                                      controls.ptrnoractvy,
                                      0,
                                      0);
    break;

    case Mouse_Shape_ToScroll:  _swix(OS_SpriteOp,
                                      _INR(0,7),

                                      292,
                                      sprite_block,
                                      "ptr_toscroll",
                                      2,
                                      controls.ptrtosactvx,
                                      controls.ptrtosactvy,
                                      0,
                                      0);
    break;

    case Mouse_Shape_Scrolling: _swix(OS_SpriteOp,
                                      _INR(0,7),

                                      292,
                                      sprite_block,
                                      "ptr_scroll",
                                      2,
                                      controls.ptrscractvx,
                                      controls.ptrscractvy,
                                      0,
                                      0);
    break;
  }
}

/*************************************************/
/* mouse_pointer_is_on()                         */
/*                                               */
/* States whether the pointer is on or off.      */
/*                                               */
/* Returns:    1 if the pointer is on, else 0.   */
/*************************************************/

int mouse_pointer_is_on(void)
{
  if (mouse_on) return 1;

  return 0;
}

/*************************************************/
/* mouse_to()                                    */
/*                                               */
/* Moves the mouse to a given screen coordinate. */
/*                                               */
/* Parameters: X coordinate to move to;          */
/*                                               */
/*             Y coordinate to move to;          */
/*                                               */
/*             1 to always move, else only move  */
/*             if mouse_used is zero (so it      */
/*             looks as if the user has not yet  */
/*             used or does not have a mouse).   */
/*************************************************/

void mouse_to(int x, int y, int now)
{
  char block[5];

  if (!now && mouse_used) return;

  block[0] = 3;
  block[1] = x & 0xff;
  block[2] = x >> 8;
  block[3] = y & 0xff;
  block[4] = y >> 8;

  _swix(OS_Word,
        _INR(0, 1),

        21,
        block);
}

/*************************************************/
/* mouse_rectangle()                             */
/*                                               */
/* Constrains the mouse pointer to a given       */
/* rectangle, or returns the last rectangle set  */
/* with this function.                           */
/*                                               */
/* Parameters: Pointer to a BBox, in which the   */
/*             new rectangle is held, or NULL to */
/*             set no new rectangle;             */
/*                                               */
/*             1 to rectangle the pointer now,   */
/*             else only rectangle it if it is   */
/*             not flagged as being used.        */
/*                                               */
/* Returns:    Pointer to a BBox, in which the   */
/*             previously set coordinates are    */
/*             stored if the BBox pointer given  */
/*             as a parameter is NULL (else the  */
/*             coordinates in the given BBox     */
/*             will be the same as the coords    */
/*             in the returned BBox).            */
/*************************************************/

BBox * mouse_rectangle(BBox * rect, int now)
{
  char        block[9];
  static BBox last_rect;

  if (!now && mouse_used) return &last_rect;

  if (rect)
  {
    block[0] = 1;
    block[1] = rect->xmin & 0xff;
    block[2] = rect->xmin >> 8;
    block[3] = rect->ymin & 0xff;
    block[4] = rect->ymin >> 8;
    block[5] = rect->xmax & 0xff;
    block[6] = rect->xmax >> 8;
    block[7] = rect->ymax & 0xff;
    block[8] = rect->ymax >> 8;

    _swix(OS_Word,
          _INR(0, 1),

          21,
          block);
  }

  last_rect = *rect;

  return &last_rect;
}