blob: e1933fad13cef0cb7055b10fabd8c24cfb5b0944 [file] [log] [blame]
/*
* Twin - A Tiny Window System
* Copyright © 2004 Keith Packard <keithp@keithp.com>
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with the Twin Library; see the file COPYING. If not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "twinint.h"
#define TWIN_ACTIVE_BG 0xd03b80ae
#define TWIN_INACTIVE_BG 0xff808080
#define TWIN_FRAME_TEXT 0xffffffff
#define TWIN_ACTIVE_BORDER 0xff606060
#define TWIN_BW 0
#define TWIN_TITLE_HEIGHT 20
#define TWIN_RESIZE_SIZE ((TWIN_TITLE_HEIGHT + 4) / 5)
#define TWIN_TITLE_BW ((TWIN_TITLE_HEIGHT + 11) / 12)
twin_window_t *
twin_window_create (twin_screen_t *screen,
twin_format_t format,
twin_window_style_t style,
twin_coord_t x,
twin_coord_t y,
twin_coord_t width,
twin_coord_t height)
{
twin_window_t *window = malloc (sizeof (twin_window_t));
twin_coord_t left, top, right, bottom;
if (!window) return NULL;
window->screen = screen;
window->style = style;
switch (window->style) {
case TwinWindowApplication:
left = TWIN_BW;
top = TWIN_BW + TWIN_TITLE_HEIGHT + TWIN_BW;
right = TWIN_BW + TWIN_RESIZE_SIZE;
bottom = TWIN_BW + TWIN_RESIZE_SIZE;
break;
case TwinWindowPlain:
default:
left = 0;
top = 0;
right = 0;
bottom = 0;
break;
}
width += left + right;
height += top + bottom;
window->client.left = left;
window->client.top = top;
window->client.right = width - right;
window->client.bottom = height - bottom;
window->pixmap = twin_pixmap_create (format, width, height);
twin_pixmap_clip (window->pixmap,
window->client.left, window->client.top,
window->client.right, window->client.bottom);
twin_pixmap_origin_to_clip (window->pixmap);
window->pixmap->window = window;
twin_pixmap_move (window->pixmap, x, y);
window->damage = window->client;
window->client_grab = TWIN_FALSE;
window->want_focus = TWIN_FALSE;
window->draw_queued = TWIN_FALSE;
window->client_data = 0;
window->name = 0;
window->draw = 0;
window->event = 0;
window->destroy = 0;
return window;
}
void
twin_window_destroy (twin_window_t *window)
{
twin_window_hide (window);
twin_pixmap_destroy (window->pixmap);
if (window->name) free (window->name);
free (window);
}
void
twin_window_show (twin_window_t *window)
{
if (window->pixmap != window->screen->top)
twin_pixmap_show (window->pixmap, window->screen, window->screen->top);
}
void
twin_window_hide (twin_window_t *window)
{
twin_pixmap_hide (window->pixmap);
}
void
twin_window_configure (twin_window_t *window,
twin_window_style_t style,
twin_coord_t x,
twin_coord_t y,
twin_coord_t width,
twin_coord_t height)
{
twin_bool_t need_repaint = TWIN_FALSE;
twin_pixmap_disable_update (window->pixmap);
if (style != window->style)
{
window->style = style;
need_repaint = TWIN_TRUE;
}
if (width != window->pixmap->width || height != window->pixmap->height)
{
twin_pixmap_t *old = window->pixmap;
int i;
window->pixmap = twin_pixmap_create (old->format, width, height);
window->pixmap->window = window;
twin_pixmap_move (window->pixmap, x, y);
if (old->screen)
twin_pixmap_show (window->pixmap, window->screen, old);
for (i = 0; i < old->disable; i++)
twin_pixmap_disable_update (window->pixmap);
twin_pixmap_destroy (old);
twin_pixmap_reset_clip (window->pixmap);
twin_pixmap_clip (window->pixmap,
window->client.left, window->client.top,
window->client.right, window->client.bottom);
twin_pixmap_origin_to_clip (window->pixmap);
}
if (x != window->pixmap->x || y != window->pixmap->y)
twin_pixmap_move (window->pixmap, x, y);
if (need_repaint)
twin_window_draw (window);
twin_pixmap_enable_update (window->pixmap);
}
void
twin_window_style_size (twin_window_style_t style,
twin_rect_t *size)
{
switch (style) {
case TwinWindowPlain:
default:
size->left = size->right = size->top = size->bottom = 0;
break;
case TwinWindowApplication:
size->left = TWIN_BW;
size->right = TWIN_BW;
size->top = TWIN_BW + TWIN_TITLE_HEIGHT + TWIN_BW;
size->bottom = TWIN_BW;
break;
}
}
void
twin_window_set_name (twin_window_t *window,
const char *name)
{
if (window->name) free (window->name);
window->name = malloc (strlen (name) + 1);
if (window->name) strcpy (window->name, name);
twin_window_draw (window);
}
static void
twin_window_frame (twin_window_t *window)
{
twin_fixed_t bw = twin_int_to_fixed (TWIN_TITLE_BW);
twin_path_t *path;
twin_fixed_t bw_2 = bw / 2;
twin_pixmap_t *pixmap = window->pixmap;
twin_fixed_t w_top = bw_2;
twin_fixed_t c_left = bw_2;
twin_fixed_t t_h = twin_int_to_fixed (window->client.top) - bw;
twin_fixed_t t_arc_1 = t_h / 3;
twin_fixed_t t_arc_2 = t_h * 2 / 3;
twin_fixed_t c_right = twin_int_to_fixed (window->client.right) - bw_2;
twin_fixed_t c_top = twin_int_to_fixed (window->client.top) - bw_2;
twin_fixed_t name_height = t_h - bw - bw_2;
twin_fixed_t icon_size = name_height * 8 / 10;
twin_fixed_t icon_y = (twin_int_to_fixed (window->client.top) -
icon_size) / 2;
twin_fixed_t menu_x = t_arc_2;
twin_fixed_t text_x = menu_x + icon_size + bw;
twin_fixed_t text_y = icon_y + icon_size;
twin_fixed_t text_width;
twin_fixed_t title_right;
twin_fixed_t close_x;
twin_fixed_t max_x;
twin_fixed_t min_x;
twin_fixed_t resize_x;
twin_fixed_t resize_y;
const char *name;
twin_pixmap_reset_clip (pixmap);
twin_pixmap_origin_to_clip (pixmap);
twin_fill (pixmap, 0x00000000, TWIN_SOURCE,
0, 0, pixmap->width, window->client.top);
path = twin_path_create ();
/* name */
name = window->name;
if (!name)
name = "Sans un nom!";
twin_path_set_font_size (path, name_height);
twin_path_set_font_style (path, TWIN_TEXT_OBLIQUE | TWIN_TEXT_UNHINTED);
text_width = twin_width_utf8 (path, name);
title_right = (text_x + text_width +
bw + icon_size +
bw + icon_size +
bw + icon_size +
t_arc_2);
if (title_right < c_right)
c_right = title_right;
close_x = c_right - t_arc_2 - icon_size;
max_x = close_x - bw - icon_size;
min_x = max_x - bw - icon_size;
resize_x = twin_int_to_fixed (window->client.right);
resize_y = twin_int_to_fixed (window->client.bottom);
/* border */
twin_path_move (path, c_left, c_top);
twin_path_draw (path, c_right, c_top);
twin_path_curve (path,
c_right, w_top + t_arc_1,
c_right - t_arc_1, w_top,
c_right - t_h, w_top);
twin_path_draw (path, c_left + t_h, w_top);
twin_path_curve (path,
c_left + t_arc_1, w_top,
c_left, w_top + t_arc_1,
c_left, c_top);
twin_path_close (path);
twin_paint_path (pixmap, TWIN_ACTIVE_BG, path);
twin_paint_stroke (pixmap, TWIN_ACTIVE_BORDER, path, bw_2 * 2);
twin_path_empty (path);
twin_pixmap_clip (pixmap,
twin_fixed_to_int (twin_fixed_floor (menu_x)),
0,
twin_fixed_to_int (twin_fixed_ceil (c_right - t_arc_2)),
window->client.top);
twin_pixmap_origin_to_clip (pixmap);
twin_path_move (path, text_x - twin_fixed_floor (menu_x), text_y);
twin_path_utf8 (path, window->name);
twin_paint_path (pixmap, TWIN_FRAME_TEXT, path);
twin_pixmap_reset_clip (pixmap);
twin_pixmap_origin_to_clip (pixmap);
/* widgets */
{
twin_matrix_t m;
twin_matrix_identity (&m);
twin_matrix_translate (&m, menu_x, icon_y);
twin_matrix_scale (&m, icon_size, icon_size);
twin_icon_draw (pixmap, TwinIconMenu, m);
twin_matrix_identity (&m);
twin_matrix_translate (&m, min_x, icon_y);
twin_matrix_scale (&m, icon_size, icon_size);
twin_icon_draw (pixmap, TwinIconMinimize, m);
twin_matrix_identity (&m);
twin_matrix_translate (&m, max_x, icon_y);
twin_matrix_scale (&m, icon_size, icon_size);
twin_icon_draw (pixmap, TwinIconMaximize, m);
twin_matrix_identity (&m);
twin_matrix_translate (&m, close_x, icon_y);
twin_matrix_scale (&m, icon_size, icon_size);
twin_icon_draw (pixmap, TwinIconClose, m);
twin_matrix_identity (&m);
twin_matrix_translate (&m, resize_x, resize_y);
twin_matrix_scale (&m,
twin_int_to_fixed (TWIN_TITLE_HEIGHT),
twin_int_to_fixed (TWIN_TITLE_HEIGHT));
twin_icon_draw (pixmap, TwinIconResize, m);
}
twin_pixmap_clip (pixmap,
window->client.left, window->client.top,
window->client.right, window->client.bottom);
twin_pixmap_origin_to_clip (pixmap);
twin_path_destroy (path);
}
void
twin_window_draw (twin_window_t *window)
{
twin_pixmap_t *pixmap = window->pixmap;
switch (window->style) {
case TwinWindowPlain:
default:
break;
case TwinWindowApplication:
twin_window_frame (window);
break;
}
/* if no draw function or no damage, return */
if (window->draw == NULL ||
(window->damage.left >= window->damage.right ||
window->damage.top >= window->damage.bottom))
return;
/* clip to damaged area and draw */
twin_pixmap_reset_clip (pixmap);
twin_pixmap_clip(pixmap,
window->damage.left, window->damage.top,
window->damage.right, window->damage.bottom);
twin_screen_disable_update(window->screen);
(*window->draw) (window);
/* damage matching screen area */
twin_pixmap_damage(pixmap,
window->damage.left, window->damage.top,
window->damage.right, window->damage.bottom);
twin_screen_enable_update(window->screen);
/* clear damage and restore clip */
window->damage.left = window->damage.right = 0;
window->damage.top = window->damage.bottom = 0;
twin_pixmap_reset_clip (pixmap);
twin_pixmap_clip (pixmap,
window->client.left, window->client.top,
window->client.right, window->client.bottom);
}
/* window keep track of local damage */
void twin_window_damage (twin_window_t *window,
twin_coord_t left, twin_coord_t top,
twin_coord_t right, twin_coord_t bottom)
{
if (left < window->client.left)
left = window->client.left;
if (top < window->client.top)
top = window->client.top;
if (right > window->client.right)
right = window->client.right;
if (bottom > window->client.bottom)
bottom = window->client.bottom;
if (window->damage.left == window->damage.right)
{
window->damage.left = left;
window->damage.right = right;
window->damage.top = top;
window->damage.bottom = bottom;
}
else
{
if (left < window->damage.left)
window->damage.left = left;
if (top < window->damage.top)
window->damage.top = top;
if (window->damage.right < right)
window->damage.right = right;
if (window->damage.bottom < bottom)
window->damage.bottom = bottom;
}
}
static twin_bool_t _twin_window_repaint (void *closure)
{
twin_window_t *window = closure;
window->draw_queued = TWIN_FALSE;
twin_window_draw(window);
return TWIN_FALSE;
}
void twin_window_queue_paint (twin_window_t *window)
{
if (!window->draw_queued) {
window->draw_queued = TWIN_TRUE;
twin_set_work (_twin_window_repaint, TWIN_WORK_PAINT, window);
}
}
twin_bool_t
twin_window_dispatch (twin_window_t *window, twin_event_t *event)
{
twin_event_t ev = *event;
twin_bool_t delegate = TWIN_TRUE;
switch (ev.kind) {
case TwinEventButtonDown:
if (window->client.left <= ev.u.pointer.x &&
ev.u.pointer.x < window->client.right &&
window->client.top <= ev.u.pointer.y &&
ev.u.pointer.y < window->client.bottom)
{
delegate = TWIN_TRUE;
window->client_grab = TWIN_TRUE;
ev.u.pointer.x -= window->client.left;
ev.u.pointer.y -= window->client.top;
}
else
delegate = TWIN_FALSE;
break;
case TwinEventButtonUp:
if (window->client_grab)
{
delegate = TWIN_TRUE;
window->client_grab = TWIN_FALSE;
ev.u.pointer.x -= window->client.left;
ev.u.pointer.y -= window->client.top;
}
else
delegate = TWIN_FALSE;
break;
case TwinEventMotion:
if (window->client_grab ||
(window->client.left <= ev.u.pointer.x &&
ev.u.pointer.x < window->client.right &&
window->client.top <= ev.u.pointer.y &&
ev.u.pointer.y < window->client.bottom))
{
delegate = TWIN_TRUE;
ev.u.pointer.x -= window->client.left;
ev.u.pointer.y -= window->client.top;
}
else
delegate = TWIN_FALSE;
break;
default:
break;
}
if (!window->event)
delegate = TWIN_FALSE;
if (delegate && (*window->event) (window, &ev))
return TWIN_TRUE;
/*
* simple window management
*/
switch (event->kind) {
case TwinEventButtonDown:
twin_window_show (window);
window->screen->button_x = event->u.pointer.x;
window->screen->button_y = event->u.pointer.y;
return TWIN_TRUE;
case TwinEventButtonUp:
window->screen->button_x = -1;
window->screen->button_y = -1;
case TwinEventMotion:
if (window->screen->button_x >= 0)
{
twin_coord_t x, y;
x = event->u.pointer.screen_x - window->screen->button_x;
y = event->u.pointer.screen_y - window->screen->button_y;
twin_window_configure (window,
window->style,
x, y,
window->pixmap->width,
window->pixmap->height);
}
return TWIN_TRUE;
default:
break;
}
return TWIN_FALSE;
}