diff --git a/ee/rpc/mouse/samples/Makefile b/ee/rpc/mouse/samples/Makefile new file mode 100644 index 000000000000..2900e0d0492a --- /dev/null +++ b/ee/rpc/mouse/samples/Makefile @@ -0,0 +1,12 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + +SAMPLE_DIR = rpc/mouse + +include $(PS2SDKSRC)/Defs.make +include $(PS2SDKSRC)/samples/Rules.samples diff --git a/ee/rpc/mouse/samples/Makefile.sample b/ee/rpc/mouse/samples/Makefile.sample new file mode 100644 index 000000000000..6e523b9db824 --- /dev/null +++ b/ee/rpc/mouse/samples/Makefile.sample @@ -0,0 +1,34 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + +EE_BIN = mouse.elf +EE_OBJS = mouse.o gfx.o square.o menu.o settings.o +EE_LIBS = -lmouse -lpatches -ldebug -ldraw -lgraph -lmath3d -lpacket -ldma + +IRX_FILES += usbd.irx ps2mouse.irx +EE_OBJS += $(IRX_FILES:.irx=_irx.o) + +all: cursor.c $(EE_BIN) + +cursor.c: cursor.raw + bin2c cursor.raw cursor.c cursor + +clean: + rm -f $(EE_BIN) $(EE_OBJS) cursor.c *_irx.c + +%_irx.c: + bin2c $(PS2SDK)/iop/irx/$*.irx $@ $*_irx + +run: $(EE_BIN) + ps2client execee host:$(EE_BIN) + +reset: + ps2client reset + +include $(PS2SDK)/samples/Makefile.pref +include $(PS2SDK)/samples/Makefile.eeglobal diff --git a/ee/rpc/mouse/samples/cursor.raw b/ee/rpc/mouse/samples/cursor.raw new file mode 100644 index 000000000000..fd05fa087df6 Binary files /dev/null and b/ee/rpc/mouse/samples/cursor.raw differ diff --git a/ee/rpc/mouse/samples/gfx.c b/ee/rpc/mouse/samples/gfx.c new file mode 100644 index 000000000000..610c3e1d4241 --- /dev/null +++ b/ee/rpc/mouse/samples/gfx.c @@ -0,0 +1,257 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# USB mouse sample: the screen. Draws rectangles, the pointer and text. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gfx.h" + +/* + * cursor.raw is the pointer image: 16x16 pixels, 4 bytes per pixel (R, G, B, A). + * The alpha goes from 0 (transparent) to 0x80 (opaque), as the GS expects. + */ +#include "cursor.c" + +extern unsigned char cursor[]; + +/* A rectangle uses 3 quadwords and the pointer 4. */ +#define PACKET_QWORDS 200 + +int screen_height; + +static packet_t *packet; +static qword_t *current; + +/* Send the packet to the GS and wait until everything in it is drawn. */ +static void send_packet(qword_t *end) +{ + dma_wait_fast(); + dma_channel_send_normal(DMA_CHANNEL_GIF, packet->data, end - packet->data, 0, 0); + draw_wait_finish(); +} + +/* + * The text is drawn by libdebug in a 32 bit framebuffer at the start of the VRAM, so the + * rectangles and the pointer are drawn in the same framebuffer. + * + * libdebug shows only the top half of that framebuffer (each row is stretched to two lines + * of the TV), so everything drawn in the bottom half stays out of the screen. + * graph_initialize() sets the display again, to show the whole framebuffer. + */ +void gfx_init(void) +{ + framebuffer_t frame; + zbuffer_t z; + texbuffer_t texbuf; + clutbuffer_t clut; + lod_t lod; + qword_t *q; + + screen_height = (graph_get_region() == GRAPH_MODE_PAL) ? 512 : 448; + + dma_channel_initialize(DMA_CHANNEL_GIF, NULL, 0); + dma_channel_fast_waits(DMA_CHANNEL_GIF); + + frame.width = SCREEN_WIDTH; + frame.height = screen_height; + frame.mask = 0; + frame.psm = GS_PSM_32; + frame.address = graph_vram_allocate(frame.width, frame.height, frame.psm, GRAPH_ALIGN_PAGE); + + graph_initialize(frame.address, frame.width, frame.height, frame.psm, 0, 0); + + /* No depth test is needed for 2D drawing. */ + z.enable = DRAW_DISABLE; + z.mask = 1; + z.method = ZTEST_METHOD_ALLPASS; + z.zsm = GS_ZBUF_32; + z.address = graph_vram_allocate(frame.width, frame.height, z.zsm, GRAPH_ALIGN_PAGE); + + /* The 16x16 pointer image is stored in a texture buffer that is 64 pixels wide. */ + texbuf.width = 64; + texbuf.psm = GS_PSM_32; + texbuf.address = graph_vram_allocate(64, CURSOR_SIZE, GS_PSM_32, GRAPH_ALIGN_BLOCK); + + packet = packet_init(PACKET_QWORDS, PACKET_NORMAL); + + q = packet->data; + q = draw_setup_environment(q, 0, &frame, &z); + q = draw_finish(q); + send_packet(q); + + q = packet->data; + q = draw_texture_transfer(q, cursor, CURSOR_SIZE, CURSOR_SIZE, GS_PSM_32, texbuf.address, texbuf.width); + q = draw_texture_flush(q); + dma_wait_fast(); + dma_channel_send_chain(DMA_CHANNEL_GIF, packet->data, q - packet->data, 0, 0); + dma_wait_fast(); + + lod.calculation = LOD_USE_K; + lod.max_level = 0; + lod.mag_filter = LOD_MAG_NEAREST; + lod.min_filter = LOD_MIN_NEAREST; + lod.l = 0; + lod.k = 0; + + texbuf.info.width = draw_log2(CURSOR_SIZE); + texbuf.info.height = draw_log2(CURSOR_SIZE); + texbuf.info.components = TEXTURE_COMPONENTS_RGBA; + texbuf.info.function = TEXTURE_FUNCTION_MODULATE; + + clut.storage_mode = CLUT_STORAGE_MODE1; + clut.start = 0; + clut.psm = 0; + clut.load_method = CLUT_NO_LOAD; + clut.address = 0; + + q = packet->data; + q = draw_texture_sampling(q, 0, &lod); + q = draw_texturebuffer(q, 0, &texbuf, &clut); + q = draw_finish(q); + send_packet(q); + + /* The pointer image has an alpha channel. */ + draw_enable_blending(); +} + +void gfx_wait_vsync(void) +{ + graph_wait_vsync(); +} + +void gfx_begin(void) +{ + current = packet->data; +} + +/* Draw what is in the packet, and start a new one. */ +static void flush(void) +{ + current = draw_finish(current); + send_packet(current); + current = packet->data; +} + +void gfx_rect(int x, int y, int width, int height, u32 color) +{ + rect_t rect; + + if (current - packet->data > PACKET_QWORDS - 8) + flush(); + + rect.v0.x = x; + rect.v0.y = y; + rect.v0.z = 0; + rect.v1.x = x + width - 1; + rect.v1.y = y + height - 1; + rect.v1.z = 0; + rect.color.r = color & 0xFF; + rect.color.g = (color >> 8) & 0xFF; + rect.color.b = (color >> 16) & 0xFF; + rect.color.a = 0x80; + rect.color.q = 1.0f; + + current = draw_rect_filled(current, 0, &rect); +} + +void gfx_pointer(int x, int y) +{ + texrect_t pointer; + + if (current - packet->data > PACKET_QWORDS - 8) + flush(); + + pointer.v0.x = x; + pointer.v0.y = y; + pointer.v0.z = 0; + pointer.t0.u = 0.0f; + pointer.t0.v = 0.0f; + pointer.v1.x = x + CURSOR_SIZE - 1; + pointer.v1.y = y + CURSOR_SIZE - 1; + pointer.t1.u = CURSOR_SIZE; + pointer.t1.v = CURSOR_SIZE; + pointer.color.r = 0x80; + pointer.color.g = 0x80; + pointer.color.b = 0x80; + pointer.color.a = 0x80; + pointer.color.q = 1.0f; + + current = draw_rect_textured(current, 0, &pointer); +} + +void gfx_end(void) +{ + flush(); +} + +static void draw_string(int x, int y, u32 color, u32 background, const char *text, int length) +{ + int i; + + scr_setbgcolor(background); + for (i = 0; i < length; i++) + scr_putchar(x + i * 8, y, color, text[i]); + scr_setbgcolor(GFX_BLACK); +} + +void gfx_text(int x, int y, u32 color, u32 background, const char *format, ...) +{ + char text[TEXT_COLUMNS + 1]; + va_list args; + + va_start(args, format); + vsnprintf(text, sizeof(text), format, args); + va_end(args); + + draw_string(x, y, color, background, text, strlen(text)); +} + +/* + * If the line was longer the last time, the rest of it is cleared, so the black background + * of the letters is drawn only where it is needed. + */ +void gfx_print_line(int line, u32 background, const char *format, ...) +{ + static int old_length[TEXT_LINES]; + char text[TEXT_COLUMNS + 1]; + va_list args; + int length, i; + + va_start(args, format); + vsnprintf(text, sizeof(text), format, args); + va_end(args); + + length = strlen(text); + for (i = length; i < old_length[line]; i++) + text[i] = ' '; + text[i] = '\0'; + + draw_string(TEXT_X, TEXT_Y + line * TEXT_LINE, GFX_WHITE, background, text, strlen(text)); + old_length[line] = length; +} + +void gfx_clear_lines(int first, int last) +{ + int line; + + for (line = first; line <= last; line++) + gfx_print_line(line, GFX_BLACK, "%s", ""); +} diff --git a/ee/rpc/mouse/samples/gfx.h b/ee/rpc/mouse/samples/gfx.h new file mode 100644 index 000000000000..fe3333fe7574 --- /dev/null +++ b/ee/rpc/mouse/samples/gfx.h @@ -0,0 +1,57 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# USB mouse sample: the screen. Draws rectangles, the pointer and text. +*/ + +#ifndef GFX_H +#define GFX_H + +#include + +#define SCREEN_WIDTH 640 +#define CURSOR_SIZE 16 + +/* The text is drawn with the 8x8 font of libdebug, TEXT_LINE is the distance between two lines. */ +#define TEXT_X 8 +#define TEXT_Y 8 +#define TEXT_LINE 12 +#define TEXT_COLUMNS 44 +#define TEXT_LINES 36 + +/* Colors of the rectangles and of the text. */ +#define GFX_RGB(r, g, b) ((u32)(r) | ((u32)(g) << 8) | ((u32)(b) << 16)) +#define GFX_BLACK GFX_RGB(0x00, 0x00, 0x00) +#define GFX_WHITE GFX_RGB(0xFF, 0xFF, 0xFF) +#define GFX_RED GFX_RGB(0xFF, 0x00, 0x00) +#define GFX_BLUE GFX_RGB(0x00, 0x00, 0xFF) +#define GFX_SELECTED GFX_RGB(0x00, 0x60, 0xC0) +#define GFX_HOVER GFX_RGB(0x40, 0x40, 0x40) +#define GFX_GUIDE GFX_RGB(0x60, 0x60, 0x60) + +/* The height of the screen is 448 on NTSC consoles and 512 on PAL consoles. Set by gfx_init(). */ +extern int screen_height; + +void gfx_init(void); +void gfx_wait_vsync(void); + +/* Rectangles and the pointer are collected between gfx_begin() and gfx_end(), and drawn at once. */ +void gfx_begin(void); +void gfx_rect(int x, int y, int width, int height, u32 color); +void gfx_pointer(int x, int y); +void gfx_end(void); + +/* Text at any position of the screen. The letters have a background of the given color. */ +void gfx_text(int x, int y, u32 color, u32 background, const char *format, ...) __attribute__((format(printf, 5, 6))); + +/* A line of text on the grid of TEXT_LINE. What was left of a longer line is cleared. */ +void gfx_print_line(int line, u32 background, const char *format, ...) __attribute__((format(printf, 3, 4))); +void gfx_clear_lines(int first, int last); + +#endif diff --git a/ee/rpc/mouse/samples/input.h b/ee/rpc/mouse/samples/input.h new file mode 100644 index 000000000000..b610d4c1d879 --- /dev/null +++ b/ee/rpc/mouse/samples/input.h @@ -0,0 +1,56 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# USB mouse sample: what the mouse did in one frame. +*/ + +#ifndef INPUT_H +#define INPUT_H + +#include +#include + +/* The mouse moves this many times slower than its raw counts, 1 would be the raw speed. */ +#define POINTER_SLOWDOWN 3 + +/* A button held for this many frames (half a second) is a long press. */ +#define LONG_PRESS_FRAMES 30 + +/* The area where the top left corner of the pointer can be. */ +typedef struct +{ + int left, top, right, bottom; +} area_t; + +/* The state of the mouse in one frame, read by main() and given to the other files. */ +typedef struct +{ + /* Position of the pointer. */ + int x, y; + /* Steps of the wheel since the last frame, positive when it is moved up. */ + int wheel; + /* Buttons that are down (PS2MOUSE_BTN1 ...) and double clicks of this frame (PS2MOUSE_BTN1DBL ...). */ + u32 buttons; + /* Buttons that were pressed or released in this frame. */ + u32 pressed; + u32 released; + /* Frames that each button (1, 2 and 3) is down. In the frame that it is released, how long it was down. */ + int hold[3]; +} input_t; + +static inline int clamp(int value, int min, int max) +{ + if (value < min) + return min; + if (value > max) + return max; + return value; +} + +#endif diff --git a/ee/rpc/mouse/samples/menu.c b/ee/rpc/mouse/samples/menu.c new file mode 100644 index 000000000000..a14abcbbdde4 --- /dev/null +++ b/ee/rpc/mouse/samples/menu.c @@ -0,0 +1,136 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# USB mouse sample: the menu of sizes that opens where the pointer is. +*/ + +#include + +#include "gfx.h" +#include "menu.h" +#include "square.h" + +#define MENU_ITEMS SQUARE_SIZE_COUNT +#define MENU_PAD 3 +#define MENU_ROW 12 +#define MENU_ITEM_W 40 +#define MENU_WIDTH (MENU_ITEM_W + 2 * MENU_PAD) +#define MENU_HEIGHT (MENU_ITEMS * MENU_ROW + 2 * MENU_PAD) + +static int item_at(const menu_t *menu, int x, int y) +{ + int row = y - menu->y - MENU_PAD; + + if (x < menu->x + MENU_PAD || x >= menu->x + MENU_PAD + MENU_ITEM_W || row < 0 || row >= MENU_ITEMS * MENU_ROW) + return -1; + return row / MENU_ROW; +} + +static int is_inside(const menu_t *menu, int x, int y) +{ + return x >= menu->x && x < menu->x + MENU_WIDTH && y >= menu->y && y < menu->y + MENU_HEIGHT; +} + +void menu_init(menu_t *menu) +{ + menu->open = 0; + menu->hover = -1; + menu->drawn = 0; +} + +void menu_open(menu_t *menu, int pointer_x, int pointer_y) +{ + menu->open = 1; + menu->x = clamp(pointer_x, 0, SCREEN_WIDTH - MENU_WIDTH); + menu->y = clamp(pointer_y, 0, screen_height - MENU_HEIGHT); + menu->hover = item_at(menu, pointer_x, pointer_y); + menu->pointer_x = pointer_x; + menu->pointer_y = pointer_y; +} + +void menu_close(menu_t *menu) +{ + menu->open = 0; + menu->hover = -1; +} + +int menu_is_open(const menu_t *menu) +{ + return menu->open; +} + +int menu_update(menu_t *menu, const input_t *input, int *size) +{ + /* The item under the pointer is the one that is marked while the pointer moves. */ + if (input->x != menu->pointer_x || input->y != menu->pointer_y) { + menu->hover = item_at(menu, input->x, input->y); + menu->pointer_x = input->x; + menu->pointer_y = input->y; + } + + /* The wheel moves the mark up and down the items. */ + if (input->wheel != 0) { + /* Without a mark, the first step of the wheel marks the top item. */ + if (menu->hover < 0) + menu->hover = (input->wheel > 0) ? input->wheel : -1; + menu->hover = clamp(menu->hover - input->wheel, 0, MENU_ITEMS - 1); + } + + if (input->pressed & (PS2MOUSE_BTN1 | PS2MOUSE_BTN2)) { + if (!is_inside(menu, input->x, input->y)) { + menu_close(menu); + } else if ((input->pressed & PS2MOUSE_BTN1) && menu->hover >= 0) { + *size = square_sizes[menu->hover]; + menu_close(menu); + return 1; + } + } + + return 0; +} + +void menu_erase(menu_t *menu) +{ + if (menu->drawn) + gfx_rect(menu->old_x, menu->old_y, MENU_WIDTH, MENU_HEIGHT, GFX_BLACK); + menu->drawn = 0; +} + +void menu_draw(menu_t *menu, int current_size) +{ + int selected = square_nearest_size(current_size); + int i, row_y; + u32 background; + + if (!menu->open) + return; + + /* White frame, black inside, and a bar behind the selected item and behind the one that is marked. */ + gfx_begin(); + gfx_rect(menu->x, menu->y, MENU_WIDTH, MENU_HEIGHT, GFX_WHITE); + gfx_rect(menu->x + 1, menu->y + 1, MENU_WIDTH - 2, MENU_HEIGHT - 2, GFX_BLACK); + for (i = 0; i < MENU_ITEMS; i++) { + row_y = menu->y + MENU_PAD + i * MENU_ROW; + if (i == selected) + gfx_rect(menu->x + MENU_PAD, row_y, MENU_ITEM_W, MENU_ROW, GFX_SELECTED); + else if (i == menu->hover) + gfx_rect(menu->x + MENU_PAD, row_y, MENU_ITEM_W, MENU_ROW, GFX_HOVER); + } + gfx_end(); + + for (i = 0; i < MENU_ITEMS; i++) { + row_y = menu->y + MENU_PAD + i * MENU_ROW; + background = (i == selected) ? GFX_SELECTED : (i == menu->hover) ? GFX_HOVER : GFX_BLACK; + gfx_text(menu->x + MENU_PAD, row_y + (MENU_ROW - 8) / 2, GFX_WHITE, background, " %3d ", square_sizes[i]); + } + + menu->drawn = 1; + menu->old_x = menu->x; + menu->old_y = menu->y; +} diff --git a/ee/rpc/mouse/samples/menu.h b/ee/rpc/mouse/samples/menu.h new file mode 100644 index 000000000000..0b690ca5a15d --- /dev/null +++ b/ee/rpc/mouse/samples/menu.h @@ -0,0 +1,50 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# USB mouse sample: the menu of sizes that opens where the pointer is. +*/ + +#ifndef MENU_H +#define MENU_H + +#include "input.h" + +typedef struct +{ + int open; + /* Top left corner. */ + int x, y; + /* The item under the pointer, or reached with the wheel. -1 if none. */ + int hover; + int pointer_x, pointer_y; + /* Where the menu was drawn the last time, to erase it. */ + int drawn; + int old_x, old_y; +} menu_t; + +void menu_init(menu_t *menu); + +/* The menu opens with its top left corner on the pointer, or as near as the screen allows. */ +void menu_open(menu_t *menu, int pointer_x, int pointer_y); +void menu_close(menu_t *menu); +int menu_is_open(const menu_t *menu); + +/* + * The pointer and the wheel choose an item, button 1 takes it. A click anywhere else closes the + * menu. Returns 1 when a size was chosen, and the menu is closed. + */ +int menu_update(menu_t *menu, const input_t *input, int *size); + +/* Between gfx_begin() and gfx_end(). */ +void menu_erase(menu_t *menu); + +/* The menu draws its own rectangles and text, so it is not between gfx_begin() and gfx_end(). */ +void menu_draw(menu_t *menu, int current_size); + +#endif diff --git a/ee/rpc/mouse/samples/mouse.c b/ee/rpc/mouse/samples/mouse.c new file mode 100644 index 000000000000..a58a136a208f --- /dev/null +++ b/ee/rpc/mouse/samples/mouse.c @@ -0,0 +1,382 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# USB mouse sample: tries everything that the mouse driver can do. The calls to the +# driver are in this file and in settings.c, the other files only draw and use input_t. +# +# The square (square.c): +# - button 1: changes its color, holding it drags it, a double click puts it in the center +# - button 2: opens the menu of sizes (menu.c), holding it changes the size all the time, +# a double click gives it the original size +# - wheel: changes its size +# The settings (settings.c), with the pointer on the text: +# - wheel: changes the setting, or button 1 to raise it and button 2 to lower it +# Everywhere else: +# - button 2: puts the pointer back in the center of the screen +# - button 3: changes the read mode (absolute or difference), a double click resets everything +# The pointer is drawn only while a mouse is plugged in, and it comes back to the place +# where it was when the mouse is plugged in again. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "input.h" +#include "gfx.h" +#include "square.h" +#include "menu.h" +#include "settings.h" + +extern unsigned char usbd_irx[] __attribute__((aligned(16))); +extern unsigned int size_usbd_irx; + +extern unsigned char ps2mouse_irx[] __attribute__((aligned(16))); +extern unsigned int size_ps2mouse_irx; + +/* Lines of the text. The settings are in SETTINGS_FIRST_LINE and the next ones. */ +#define LINE_TITLE 0 +#define LINE_ERROR 1 +#define LINE_MICE 2 +#define LINE_MODE 3 +#define LINE_POINTER 4 +#define LINE_WHEEL 5 +#define LINE_BUTTONS 6 +#define LINE_DOUBLE 7 +#define LINE_HELP 29 + +typedef struct +{ + int mode; + int pointer_x, pointer_y; + int old_x, old_y; + /* What is left of the movement after dividing it by POINTER_SLOWDOWN, in difference mode. */ + int rest_x, rest_y; + int wheel_total; + const char *last_double_click; + area_t area; + square_t square; + menu_t menu; + settings_t settings; +} app_t; + +static void load_module(const char *name, void *irx, unsigned int size) +{ + int ret = SifExecModuleBuffer(irx, size, 0, NULL, NULL); + + if (ret < 0) { + scr_printf("Failed to load %s: %d\n", name, ret); + SleepThread(); + } +} + +static const char *state(u32 buttons, u32 button) +{ + return (buttons & button) ? "down" : "up"; +} + +/* + * In absolute mode the driver keeps the position in raw counts, inside the area of the pointer. + * In difference mode the driver has no position: what is set there would be read as a movement. + */ +static void set_driver_position(const app_t *app) +{ + if (app->mode == PS2MOUSE_READMODE_ABS) + PS2MouseSetPosition((app->pointer_x - app->area.left) * POINTER_SLOWDOWN, (app->pointer_y - app->area.top) * POINTER_SLOWDOWN); +} + +/* Put the pointer somewhere, and tell the driver. */ +static void move_pointer(app_t *app, int x, int y) +{ + app->pointer_x = clamp(x, app->area.left, app->area.right); + app->pointer_y = clamp(y, app->area.top, app->area.bottom); + app->rest_x = 0; + app->rest_y = 0; + set_driver_position(app); +} + +static void change_read_mode(app_t *app) +{ + app->mode = (PS2MouseGetReadMode() == PS2MOUSE_READMODE_ABS) ? PS2MOUSE_READMODE_DIFF : PS2MOUSE_READMODE_ABS; + PS2MouseSetReadMode(app->mode); + move_pointer(app, app->pointer_x, app->pointer_y); +} + +/* Everything as when the sample starts. */ +static void reset_all(app_t *app) +{ + PS2MouseReset(); + + app->mode = PS2MOUSE_READMODE_ABS; + PS2MouseSetReadMode(app->mode); + settings_reset(&app->settings, &app->area); + + move_pointer(app, SCREEN_WIDTH / 2, screen_height / 2); + app->wheel_total = 0; + app->last_double_click = "none"; + + square_reset(&app->square); + menu_close(&app->menu); +} + +/* Fills the input of this frame, from what the driver says and from the last frame. */ +static void make_input(input_t *input, const PS2MouseData *data, const app_t *app, u32 *previous_buttons, int *held_frames) +{ + u32 down = data->buttons & 0xFF; + int i; + + input->x = app->pointer_x; + input->y = app->pointer_y; + input->wheel = (int)data->wheel; + input->buttons = data->buttons; + input->pressed = down & ~*previous_buttons & 0x7; + input->released = *previous_buttons & ~down & 0x7; + + for (i = 0; i < 3; i++) { + if (down & (1 << i)) + held_frames[i]++; + input->hold[i] = held_frames[i]; + if (input->released & (1 << i)) + held_frames[i] = 0; + } + + *previous_buttons = down; +} + +/* Move the pointer with what the mouse says. */ +static void update_pointer(app_t *app, const PS2MouseData *data) +{ + int step_x, step_y; + + if (app->mode == PS2MOUSE_READMODE_ABS) { + app->pointer_x = app->area.left + data->x / POINTER_SLOWDOWN; + app->pointer_y = app->area.top + data->y / POINTER_SLOWDOWN; + } else { + /* Keep what is left after the division, so that slow movements are not lost. */ + app->rest_x += data->x; + app->rest_y += data->y; + step_x = app->rest_x / POINTER_SLOWDOWN; + step_y = app->rest_y / POINTER_SLOWDOWN; + app->rest_x -= step_x * POINTER_SLOWDOWN; + app->rest_y -= step_y * POINTER_SLOWDOWN; + app->pointer_x += step_x; + app->pointer_y += step_y; + } + app->pointer_x = clamp(app->pointer_x, app->area.left, app->area.right); + app->pointer_y = clamp(app->pointer_y, app->area.top, app->area.bottom); +} + +/* Give the input to the square, the menu and the settings. */ +static void handle_input(app_t *app, const input_t *input) +{ + int consumed = 0; + int open_menu = 0; + int area_changed = 0; + int size; + int on_square = square_contains(&app->square, input->x, input->y); + int settings_active = !menu_is_open(&app->menu) && !square_is_busy(&app->square) && !on_square; + + if ((input->buttons & PS2MOUSE_BTN2DBL) && on_square) { + /* A double click of button 2 on the square: it gets its original size. */ + menu_close(&app->menu); + square_double_click_button2(&app->square); + consumed = 1; + } else if (menu_is_open(&app->menu)) { + if (menu_update(&app->menu, input, &size)) + square_set_size(&app->square, size); + consumed = 1; + } else { + consumed = settings_update(&app->settings, input, settings_active, &app->area, &area_changed); + if (!consumed) { + consumed = square_update(&app->square, input, &open_menu); + if (open_menu) + menu_open(&app->menu, input->x, input->y); + } + } + + if (area_changed) + move_pointer(app, app->pointer_x, app->pointer_y); + + /* Button 2 anywhere else puts the pointer in the center of the screen. */ + if (!consumed && (input->pressed & PS2MOUSE_BTN2)) + move_pointer(app, SCREEN_WIDTH / 2, screen_height / 2); +} + +static void draw_text(app_t *app, const PS2MouseData *data, u32 mice, u32 version, int mouse_present) +{ + gfx_print_line(LINE_TITLE, GFX_BLACK, "PS2 USB mouse sample (driver 0x%x)", (unsigned int)version); + gfx_print_line(LINE_MICE, GFX_BLACK, "Mice connected: %u", (unsigned int)mice); + + if (!mouse_present) { + gfx_print_line(LINE_ERROR, GFX_BLACK, "%s", ""); + gfx_print_line(LINE_MODE, GFX_BLACK, "Waiting for a USB mouse..."); + gfx_clear_lines(LINE_POINTER, TEXT_LINES - 1); + return; + } + + gfx_print_line(LINE_MODE, GFX_BLACK, "Read mode: %s", (PS2MouseGetReadMode() == PS2MOUSE_READMODE_ABS) ? "absolute" : "difference"); + gfx_print_line(LINE_POINTER, GFX_BLACK, "Pointer: x=%d y=%d", app->pointer_x, app->pointer_y); + gfx_print_line(LINE_WHEEL, GFX_BLACK, "Wheel: %d", app->wheel_total); + gfx_print_line(LINE_BUTTONS, GFX_BLACK, "Buttons: 1=%s 2=%s 3=%s", state(data->buttons, PS2MOUSE_BTN1), + state(data->buttons, PS2MOUSE_BTN2), state(data->buttons, PS2MOUSE_BTN3)); + gfx_print_line(LINE_DOUBLE, GFX_BLACK, "Last double click: %s", app->last_double_click); + settings_print(&app->settings); + + gfx_print_line(LINE_HELP, GFX_BLACK, "Button 1: color; hold: drag; twice: home"); + gfx_print_line(LINE_HELP + 1, GFX_BLACK, "Button 2: size menu; hold: cycle; twice: 64"); + gfx_print_line(LINE_HELP + 2, GFX_BLACK, "Button 2 elsewhere: center the pointer"); + gfx_print_line(LINE_HELP + 3, GFX_BLACK, "Wheel on the square: change its size"); + gfx_print_line(LINE_HELP + 4, GFX_BLACK, "Wheel on a setting: change it"); + gfx_print_line(LINE_HELP + 5, GFX_BLACK, "Button 3: read mode; twice: reset all"); +} + +int main(int argc, char *argv[]) +{ + static app_t app; + PS2MouseData data; + input_t input; + u32 previous_buttons = 0; + u32 mice; + u32 version; + int held_frames[3] = {0, 0, 0}; + int mouse_present = 0; + + (void)argc; + (void)argv; + + memset(&data, 0, sizeof(data)); + memset(&input, 0, sizeof(input)); + + init_scr(); + + SifInitRpc(0); + while (!SifIopReset(NULL, 0)) {}; + while (!SifIopSync()) {}; + SifInitRpc(0); + + sbv_patch_enable_lmb(); + sbv_patch_disable_prefix_check(); + + load_module("usbd.irx", usbd_irx, size_usbd_irx); + load_module("ps2mouse.irx", ps2mouse_irx, size_ps2mouse_irx); + + if (PS2MouseInit() < 0) { + scr_printf("PS2MouseInit failed\n"); + SleepThread(); + } + + version = PS2MouseGetVersion(); + + scr_clear(); + gfx_init(); + + /* Everything starts in the center of the screen. */ + app.mode = PS2MOUSE_READMODE_ABS; + app.last_double_click = "none"; + settings_init(&app.settings, &app.area); + square_init(&app.square); + menu_init(&app.menu); + PS2MouseSetReadMode(app.mode); + app.pointer_x = SCREEN_WIDTH / 2; + app.pointer_y = screen_height / 2; + app.old_x = app.pointer_x; + app.old_y = app.pointer_y; + set_driver_position(&app); + + while (1) { + gfx_wait_vsync(); + + mice = PS2MouseEnum(); + + if (mice == 0 && mouse_present) { + /* The mouse was unplugged, the pointer is not drawn until it is plugged in again. */ + mouse_present = 0; + square_cancel(&app.square); + menu_close(&app.menu); + app.settings.hover = -1; + memset(&data, 0, sizeof(data)); + } else if (mice != 0 && !mouse_present) { + /* A mouse was plugged in, the pointer starts where it was the last time. */ + mouse_present = 1; + move_pointer(&app, app.pointer_x, app.pointer_y); + previous_buttons = 0; + memset(held_frames, 0, sizeof(held_frames)); + } + + if (mouse_present) { + if (PS2MouseRead(&data) < 0) { + gfx_print_line(LINE_ERROR, GFX_BLACK, "PS2MouseRead failed"); + continue; + } + gfx_print_line(LINE_ERROR, GFX_BLACK, "%s", ""); + + /* Button 3 changes the read mode, and two clicks reset everything. */ + if ((data.buttons & 0x7 & ~previous_buttons) & PS2MOUSE_BTN3) { + change_read_mode(&app); + /* The data of this read belongs to the old mode, do not move the pointer with it. */ + data.x = (app.pointer_x - app.area.left) * POINTER_SLOWDOWN; + data.y = (app.pointer_y - app.area.top) * POINTER_SLOWDOWN; + if (app.mode == PS2MOUSE_READMODE_DIFF) { + data.x = 0; + data.y = 0; + } + } + + update_pointer(&app, &data); + app.wheel_total += (int)data.wheel; + make_input(&input, &data, &app, &previous_buttons, held_frames); + + if (data.buttons & PS2MOUSE_BTN3DBL) + reset_all(&app); + else + handle_input(&app, &input); + + if (data.buttons & PS2MOUSE_BTN1DBL) + app.last_double_click = "button 1"; + if (data.buttons & PS2MOUSE_BTN2DBL) + app.last_double_click = "button 2"; + if (data.buttons & PS2MOUSE_BTN3DBL) + app.last_double_click = "button 3"; + } + + /* Erase what was drawn the last time, and draw the square, the text, the menu and the pointer, in this order. */ + gfx_begin(); + square_erase(&app.square); + menu_erase(&app.menu); + settings_erase_guide(&app.settings); + gfx_rect(app.old_x, app.old_y, CURSOR_SIZE, CURSOR_SIZE, GFX_BLACK); + gfx_end(); + + gfx_begin(); + square_draw(&app.square); + settings_draw_guide(&app.settings, &app.area); + gfx_end(); + + draw_text(&app, &data, mice, version, mouse_present); + menu_draw(&app.menu, app.square.size); + + if (mouse_present) { + gfx_begin(); + gfx_pointer(app.pointer_x, app.pointer_y); + gfx_end(); + } + + app.old_x = app.pointer_x; + app.old_y = app.pointer_y; + } + + return 0; +} diff --git a/ee/rpc/mouse/samples/settings.c b/ee/rpc/mouse/samples/settings.c new file mode 100644 index 000000000000..d759ea52489d --- /dev/null +++ b/ee/rpc/mouse/samples/settings.c @@ -0,0 +1,244 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# USB mouse sample: the settings of the driver that are changed with the mouse. +*/ + +#include +#include +#include + +#include "gfx.h" +#include "settings.h" + +/* The original settings of the driver. */ +#define DEFAULT_ACCEL_TENTHS 10 +#define DEFAULT_THRESHOLD 65536 +#define DEFAULT_DOUBLE_CLICK 500 + +/* The acceleration changes by half a unit, and the double click time by 100 milliseconds. */ +#define ACCEL_MIN_TENTHS 5 +#define ACCEL_MAX_TENTHS 40 +#define DOUBLE_CLICK_MIN 100 +#define DOUBLE_CLICK_MAX 1000 +#define DOUBLE_CLICK_STEP 100 + +/* Distance between the pointer and the borders of the screen when the margin is on. */ +#define MARGIN 32 + +/* + * A movement of the mouse that is as big as the threshold is multiplied by the acceleration. + * The original threshold is so big that the acceleration is never used. + */ +static const u32 thresholds[] = {1, 2, 4, 8, 16, 32, DEFAULT_THRESHOLD}; +#define THRESHOLD_COUNT (sizeof(thresholds) / sizeof(thresholds[0])) + +static void make_area(int with_margin, area_t *area) +{ + int margin = with_margin ? MARGIN : 0; + + area->left = margin; + area->top = margin; + area->right = SCREEN_WIDTH - margin - CURSOR_SIZE; + area->bottom = screen_height - margin - CURSOR_SIZE; +} + +/* Read what the driver has, to show it. */ +static void read_driver(settings_t *settings) +{ + int min_x, max_x, min_y, max_y; + + settings->accel_tenths = (int)(PS2MouseGetAccel() * 10.0f + 0.5f); + settings->threshold = PS2MouseGetThres(); + settings->double_click_time = PS2MouseGetDblClickTIme(); + + PS2MouseGetBoundary(&min_x, &max_x, &min_y, &max_y); + settings->boundary_width = (max_x - min_x) / POINTER_SLOWDOWN + CURSOR_SIZE; + settings->boundary_height = (max_y - min_y) / POINTER_SLOWDOWN + CURSOR_SIZE; +} + +/* In absolute mode, the driver keeps the pointer inside the boundary. */ +static void write_boundary(settings_t *settings, area_t *area) +{ + make_area(settings->margin, area); + PS2MouseSetBoundary(0, (area->right - area->left) * POINTER_SLOWDOWN, 0, (area->bottom - area->top) * POINTER_SLOWDOWN); +} + +void settings_reset(settings_t *settings, area_t *area) +{ + settings->margin = 0; + settings->hover = -1; + + PS2MouseSetAccel(DEFAULT_ACCEL_TENTHS / 10.0f); + PS2MouseSetThres(DEFAULT_THRESHOLD); + PS2MouseSetDblClickTime(DEFAULT_DOUBLE_CLICK); + write_boundary(settings, area); + + if (settings->guide_drawn) + settings->erase_guide = 1; + + read_driver(settings); +} + +void settings_init(settings_t *settings, area_t *area) +{ + settings->guide_drawn = 0; + settings->erase_guide = 0; + settings_reset(settings, area); +} + +/* The text of a setting. */ +static void format_setting(const settings_t *settings, int index, char *text, size_t size) +{ + switch (index) { + case 0: + snprintf(text, size, "Acceleration: %d.%d", settings->accel_tenths / 10, settings->accel_tenths % 10); + break; + case 1: + if (settings->threshold >= DEFAULT_THRESHOLD) + snprintf(text, size, "Threshold: off"); + else + snprintf(text, size, "Threshold: %u", (unsigned int)settings->threshold); + break; + case 2: + snprintf(text, size, "Double click time: %u ms", (unsigned int)settings->double_click_time); + break; + default: + snprintf(text, size, "Boundary: %dx%d", settings->boundary_width, settings->boundary_height); + break; + } +} + +/* The setting under the pointer, or -1. */ +static int setting_at(const settings_t *settings, int x, int y) +{ + char text[TEXT_COLUMNS + 1]; + int i, line_y; + + for (i = 0; i < SETTINGS_COUNT; i++) { + line_y = TEXT_Y + (SETTINGS_FIRST_LINE + i) * TEXT_LINE; + format_setting(settings, i, text, sizeof(text)); + if (x >= TEXT_X && x < TEXT_X + (int)strlen(text) * 8 && y >= line_y - 2 && y < line_y + TEXT_LINE - 2) + return i; + } + return -1; +} + +/* Raise (1) or lower (-1) a setting. */ +static void change(settings_t *settings, int index, int direction, area_t *area, int *area_changed) +{ + int i, nearest; + u32 distance, best; + + switch (index) { + case 0: + PS2MouseSetAccel(clamp(settings->accel_tenths + direction * 5, ACCEL_MIN_TENTHS, ACCEL_MAX_TENTHS) / 10.0f); + break; + case 1: + /* The threshold goes through the list, starting at the item that is nearest to the current one. */ + nearest = 0; + best = 0xFFFFFFFF; + for (i = 0; i < (int)THRESHOLD_COUNT; i++) { + distance = (settings->threshold > thresholds[i]) ? settings->threshold - thresholds[i] : thresholds[i] - settings->threshold; + if (distance < best) { + best = distance; + nearest = i; + } + } + PS2MouseSetThres(thresholds[clamp(nearest + direction, 0, (int)THRESHOLD_COUNT - 1)]); + break; + case 2: + PS2MouseSetDblClickTime(clamp((int)settings->double_click_time + direction * DOUBLE_CLICK_STEP, DOUBLE_CLICK_MIN, DOUBLE_CLICK_MAX)); + break; + default: + /* The boundary is the whole screen, or the screen without a margin. */ + settings->margin = (direction > 0); + write_boundary(settings, area); + *area_changed = 1; + if (settings->guide_drawn && !settings->margin) + settings->erase_guide = 1; + break; + } + + read_driver(settings); +} + +int settings_update(settings_t *settings, const input_t *input, int active, area_t *area, int *area_changed) +{ + int direction = 0; + int index = active ? setting_at(settings, input->x, input->y) : -1; + int steps; + + settings->hover = index; + if (index < 0) + return 0; + + if (input->wheel > 0) + direction = 1; + else if (input->wheel < 0) + direction = -1; + if (input->pressed & PS2MOUSE_BTN1) + direction = 1; + if (input->pressed & PS2MOUSE_BTN2) + direction = -1; + + if (direction != 0) { + steps = (input->wheel != 0 && !(input->pressed & (PS2MOUSE_BTN1 | PS2MOUSE_BTN2))) ? clamp(input->wheel * direction, 1, 5) : 1; + while (steps-- > 0) + change(settings, index, direction, area, area_changed); + } + + return 1; +} + +void settings_print(settings_t *settings) +{ + char text[TEXT_COLUMNS + 1]; + int i; + + for (i = 0; i < SETTINGS_COUNT; i++) { + format_setting(settings, i, text, sizeof(text)); + gfx_print_line(SETTINGS_FIRST_LINE + i, (i == settings->hover) ? GFX_HOVER : GFX_BLACK, "%s", text); + } +} + +/* A thin frame around the area of the pointer, drawn with four rectangles. */ +static void frame(const area_t *area, u32 color) +{ + int right = area->right + CURSOR_SIZE; + int bottom = area->bottom + CURSOR_SIZE; + + gfx_rect(area->left - 1, area->top - 1, right - area->left + 2, 1, color); + gfx_rect(area->left - 1, bottom, right - area->left + 2, 1, color); + gfx_rect(area->left - 1, area->top, 1, bottom - area->top, color); + gfx_rect(right, area->top, 1, bottom - area->top, color); +} + +void settings_erase_guide(settings_t *settings) +{ + area_t area; + + if (!settings->erase_guide) + return; + + make_area(1, &area); + frame(&area, GFX_BLACK); + + settings->erase_guide = 0; + settings->guide_drawn = 0; +} + +void settings_draw_guide(settings_t *settings, const area_t *area) +{ + if (!settings->margin) + return; + + frame(area, GFX_GUIDE); + settings->guide_drawn = 1; +} diff --git a/ee/rpc/mouse/samples/settings.h b/ee/rpc/mouse/samples/settings.h new file mode 100644 index 000000000000..0536f090c30c --- /dev/null +++ b/ee/rpc/mouse/samples/settings.h @@ -0,0 +1,59 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# USB mouse sample: the settings of the driver that are changed with the mouse. +*/ + +#ifndef SETTINGS_H +#define SETTINGS_H + +#include "input.h" + +/* Number of settings, and the line of the text where the first one is. */ +#define SETTINGS_COUNT 4 +#define SETTINGS_FIRST_LINE 9 + +typedef struct +{ + /* What the driver says, read after each change. */ + int accel_tenths; + u32 threshold; + u32 double_click_time; + int boundary_width, boundary_height; + /* The pointer stays away from the borders of the screen. */ + int margin; + /* The setting under the pointer, or -1. */ + int hover; + /* The frame that shows the margin is on the screen, or it has to be erased. */ + int guide_drawn; + int erase_guide; +} settings_t; + +/* Sets the driver to the original settings and gives the area of the pointer. */ +void settings_init(settings_t *settings, area_t *area); + +/* The same, for the reset of everything. */ +void settings_reset(settings_t *settings, area_t *area); + +/* + * Acceleration, threshold, double click time and boundary. The wheel changes the setting that is + * under the pointer, button 1 raises it and button 2 lowers it. + * Only when active, the mouse can change them. Returns 1 if the pointer is on a setting. + * When the boundary changes, area_changed is set to 1 and area has the new area. + */ +int settings_update(settings_t *settings, const input_t *input, int active, area_t *area, int *area_changed); + +/* The text of the settings. */ +void settings_print(settings_t *settings); + +/* Between gfx_begin() and gfx_end(). */ +void settings_erase_guide(settings_t *settings); +void settings_draw_guide(settings_t *settings, const area_t *area); + +#endif diff --git a/ee/rpc/mouse/samples/square.c b/ee/rpc/mouse/samples/square.c new file mode 100644 index 000000000000..0d688e4cae09 --- /dev/null +++ b/ee/rpc/mouse/samples/square.c @@ -0,0 +1,180 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# USB mouse sample: the square that the buttons and the wheel of the mouse act on. +*/ + +#include + +#include "gfx.h" +#include "square.h" + +/* Pixels that the size changes for each step of the wheel. */ +#define WHEEL_STEP 4 + +/* Frames between two sizes, while button 2 is held. */ +#define CYCLE_FRAMES 20 + +const int square_sizes[SQUARE_SIZE_COUNT] = {128, 64, 32, 16, 8, 4, 2}; + +int square_nearest_size(int size) +{ + int best = 0; + int i, distance, best_distance = 1000; + + for (i = 0; i < SQUARE_SIZE_COUNT; i++) { + distance = (size > square_sizes[i]) ? size - square_sizes[i] : square_sizes[i] - size; + if (distance < best_distance) { + best = i; + best_distance = distance; + } + } + return best; +} + +static void keep_inside_screen(square_t *square) +{ + square->x = clamp(square->x, 0, SCREEN_WIDTH - square->size); + square->y = clamp(square->y, 0, screen_height - square->size); +} + +static void go_to_center(square_t *square) +{ + square->x = (SCREEN_WIDTH - square->size) / 2; + square->y = (screen_height - square->size) / 2; +} + +void square_reset(square_t *square) +{ + square->size = SQUARE_SIZE; + square->is_blue = 0; + square_cancel(square); + go_to_center(square); +} + +void square_init(square_t *square) +{ + square_reset(square); + square->old_x = square->x; + square->old_y = square->y; + square->old_size = square->size; +} + +void square_cancel(square_t *square) +{ + square->dragging = 0; + square->right_down = 0; + square->ignore_release = 0; + square->cycle_direction = 1; +} + +int square_contains(const square_t *square, int x, int y) +{ + return x >= square->x && x < square->x + square->size && y >= square->y && y < square->y + square->size; +} + +int square_is_busy(const square_t *square) +{ + return square->dragging || square->right_down; +} + +void square_set_size(square_t *square, int size) +{ + int center_x = square->x + square->size / 2; + int center_y = square->y + square->size / 2; + + square->size = clamp(size, SQUARE_MIN_SIZE, SQUARE_MAX_SIZE); + square->x = center_x - square->size / 2; + square->y = center_y - square->size / 2; + keep_inside_screen(square); +} + +void square_double_click_button2(square_t *square) +{ + square_set_size(square, SQUARE_SIZE); + square->right_down = 1; + square->ignore_release = 1; + square->cycle_direction = 1; +} + +int square_update(square_t *square, const input_t *input, int *open_menu) +{ + int on_square = square_contains(square, input->x, input->y); + + /* Button 1 changes the color and holds the square, to drag it. */ + if ((input->pressed & PS2MOUSE_BTN1) && on_square) { + square->is_blue = !square->is_blue; + square->dragging = 1; + square->grab_x = input->x - square->x; + square->grab_y = input->y - square->y; + } + if (!(input->buttons & PS2MOUSE_BTN1)) + square->dragging = 0; + + /* A double click of button 1 puts the square back in the center. */ + if ((input->buttons & PS2MOUSE_BTN1DBL) && on_square) { + square->dragging = 0; + go_to_center(square); + } + + if (square->dragging) { + square->x = input->x - square->grab_x; + square->y = input->y - square->grab_y; + } + + /* + * Button 2: a click asks for the menu of sizes when it is released. Holding it goes through the + * sizes of the menu, from the one that is nearest to the current size to the smallest, then to + * the biggest, and so on. It stops while the pointer is not on the square. + */ + if ((input->pressed & PS2MOUSE_BTN2) && on_square) { + square->right_down = 1; + square->ignore_release = 0; + square->cycle_direction = 1; + } + if (square->right_down) { + if ((input->buttons & PS2MOUSE_BTN2) && on_square && input->hold[1] >= LONG_PRESS_FRAMES && + (input->hold[1] - LONG_PRESS_FRAMES) % CYCLE_FRAMES == 0) { + int item = square_nearest_size(square->size); + + if (item + square->cycle_direction < 0 || item + square->cycle_direction >= SQUARE_SIZE_COUNT) + square->cycle_direction = -square->cycle_direction; + item = clamp(item + square->cycle_direction, 0, SQUARE_SIZE_COUNT - 1); + square_set_size(square, square_sizes[item]); + } + if (input->released & PS2MOUSE_BTN2) { + if (!square->ignore_release && input->hold[1] < LONG_PRESS_FRAMES) + *open_menu = 1; + square->right_down = 0; + square->ignore_release = 0; + } + } + + /* The wheel changes the size while the pointer is on the square, and stops when it is not. */ + if (input->wheel != 0 && on_square && !square->dragging && !square->right_down) + square_set_size(square, square->size + input->wheel * WHEEL_STEP); + + keep_inside_screen(square); + + return on_square; +} + +void square_erase(const square_t *square) +{ + gfx_rect(square->old_x, square->old_y, square->old_size, square->old_size, GFX_BLACK); +} + +void square_draw(square_t *square) +{ + gfx_rect(square->x, square->y, square->size, square->size, square->is_blue ? GFX_BLUE : GFX_RED); + + square->old_x = square->x; + square->old_y = square->y; + square->old_size = square->size; +} diff --git a/ee/rpc/mouse/samples/square.h b/ee/rpc/mouse/samples/square.h new file mode 100644 index 000000000000..8ea33aff2f69 --- /dev/null +++ b/ee/rpc/mouse/samples/square.h @@ -0,0 +1,79 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# USB mouse sample: the square that the buttons and the wheel of the mouse act on. +*/ + +#ifndef SQUARE_H +#define SQUARE_H + +#include "input.h" + +#define SQUARE_SIZE 64 +#define SQUARE_MIN_SIZE 2 +#define SQUARE_MAX_SIZE 128 +#define SQUARE_SIZE_COUNT 7 + +/* The sizes that the menu offers and that holding button 2 goes through, from the biggest to the smallest. */ +extern const int square_sizes[SQUARE_SIZE_COUNT]; + +/* The item of square_sizes that is nearest to the given size. */ +int square_nearest_size(int size); + +typedef struct +{ + int x, y, size; + int is_blue; + /* Button 1 is holding the square, and where in the square. */ + int dragging; + int grab_x, grab_y; + /* Button 2 was pressed on the square, and it must not open the menu when it is released. */ + int right_down; + int ignore_release; + /* While button 2 is held the size goes through square_sizes: toward the smallest (1) or the biggest (-1). */ + int cycle_direction; + /* Where the square was drawn the last time, to erase it. */ + int old_x, old_y, old_size; +} square_t; + +/* The square in the center of the screen, with its original size and color. */ +void square_init(square_t *square); + +/* The same, but the old square is still erased. */ +void square_reset(square_t *square); + +/* Stop what the buttons were doing, for when the mouse is unplugged. */ +void square_cancel(square_t *square); + +int square_contains(const square_t *square, int x, int y); + +/* The square is busy while it is dragged or while button 2 is held on it. */ +int square_is_busy(const square_t *square); + +/* Change the size, the center of the square stays. */ +void square_set_size(square_t *square, int size); + +/* Button 2 was double clicked on the square: it gets its original size. */ +void square_double_click_button2(square_t *square); + +/* + * Button 1: changes the color, holding it drags the square, a double click puts it back in + * the center. + * Button 2: a click asks for the menu of sizes (open_menu), holding it goes through the sizes + * of the menu, up and down, while the pointer is on the square. + * Wheel: changes the size. + * Returns 1 if the pointer is on the square. + */ +int square_update(square_t *square, const input_t *input, int *open_menu); + +/* Between gfx_begin() and gfx_end(). */ +void square_erase(const square_t *square); +void square_draw(square_t *square); + +#endif diff --git a/samples/Makefile_sample b/samples/Makefile_sample index f1f78519b791..0f4e7e617c75 100644 --- a/samples/Makefile_sample +++ b/samples/Makefile_sample @@ -45,6 +45,7 @@ SUBDIRS += rpc/audsrv/testcd SUBDIRS += rpc/filexio #SUBDIRS += rpc/camera #TODO: not modified for updated newlib #SUBDIRS += rpc/memorycard #TODO: not modified for updated newlib +SUBDIRS += rpc/mouse SUBDIRS += rpc/mtap SUBDIRS += rpc/pad SUBDIRS += rpc/padx