feat: Populate repo

This commit is contained in:
2023-03-29 14:02:05 +02:00
commit 28bec5ebb8
168 changed files with 17860 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
# Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
include(CheckIncludeFiles)
add_executable(vtouchpad vtouchpad.c)
target_compile_definitions(vtouchpad PRIVATE DISABLE_VERBOSE)
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
target_sources(vtouchpad PRIVATE mouse_x11.c)
#
# If FreeBSD, add /usr/local/include to the search path too,
# see: https://wiki.freebsd.org/WarnerLosh/UsrLocal
#
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
set(CMAKE_REQUIRED_INCLUDES "/usr/local/include;/usr/include")
target_link_directories(vtouchpad PUBLIC /usr/local/lib)
target_include_directories(vtouchpad PUBLIC /usr/local/include)
endif()
CHECK_INCLUDE_FILES("xdo.h" HAVE_XDO_H)
if (NOT HAVE_XDO_H)
message(WARNING "libxdo/xdotool not found, trying to use XTest exts...")
CHECK_INCLUDE_FILES("X11/extensions/XTest.h" HAVE_XTEST_H)
if (NOT HAVE_XTEST_H)
message(WARNING "Unable to find XTest.h, vtouchpad is unable "
"to work in your system, disabling build...")
set_target_properties(vtouchpad
PROPERTIES EXCLUDE_FROM_ALL True)
endif ()
target_link_libraries(vtouchpad X11 Xtst ws pthread)
else()
target_compile_definitions(vtouchpad PUBLIC HAVE_XDOTOOL)
target_link_libraries(vtouchpad X11 xdo ws pthread)
endif()
elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
target_sources(vtouchpad PRIVATE mouse_win.c)
target_link_libraries(vtouchpad ws ws2_32)
else()
message(WARNING "Operating System '${CMAKE_SYSTEM_NAME}' not supported!")
set_target_properties(vtouchpad
PROPERTIES EXCLUDE_FROM_ALL True)
endif ()

View File

@@ -0,0 +1,50 @@
# vtouchpad
This example implements a simple 'virtual touchpad' that allows a web client to control a remote computer's mouse via wsServer/websocket.
## Features
<p align="center">
<img align="center" src="https://i.imgur.com/sOTqwWI.png" alt="vtouchpad example">
<br>
vtouchpad example
</p>
The larger area represents the main touchpad, move the cursor over it to move the mouse. Left and right clicks within this area also work as expected.
The two smaller buttons represent the left and right buttons respectively.
## Building
Since mouse movement is OS-dependent, this example works on a limited number of systems, currently Linux, FreeBSD and Windows.
**Linux and FreeBSD builds:**
vtouchpad requires the environment to run under X11, and requires libX11, libXtst or libxdo (comes with xdotool).
A build for Ubuntu and the like:
```bash
$ sudo apt install libxdo-dev
$ cd wsServer/
$ mkdir build && cd build/
$ cmake ..
$ make
$ ./examples/vtouchpad/vtouchpad
```
FreeBSD (although xdotool is available, the package is currently without maintainer, so its use is discouraged; a normal build already works as expected):
```bash
$ cd wsServer/
$ mkdir build && cd build/
$ cmake ..
$ make
$ ./examples/vtouchpad/vtouchpad
```
**Windows builds:**
It does not require any special libraries, and should work on any version above Windows 2000.
```bash
$ cd wsServer/
$ mkdir build && cd build/
$ cmake .. -G "MinGW Makefiles"
$ mingw32-make
$ ./examples/vtouchpad/vtouchpad.exe
```

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#if !defined(_WIN32)
#error "Expected Windows here!"
#endif
#include <windows.h>
#include "vtouchpad.h"
/**
* @dir examples/vtouchpad
* @brief Touchpad example directory
*
* @file mouse_win.c
* @brief Mouse windows implementation.
*/
/**
* Stub structure.
*/
struct mouse { int stub; };
/**
* @brief Do nothing, stub.
*
* @return Returns always 0.
*/
mouse_t *mouse_new(void)
{
return (NULL);
}
/**
* @brief Do nothing, stub.
*
* @param mouse Mouse structure pointer.
*
* @return Returns always NULL.
*/
void *mouse_free(mouse_t *mouse)
{
((void)mouse);
return (NULL);
}
/**
* @brief Moves the mouse pointed by @p mouse to the
* offsets @p x_off and @p y_off.
*
* @param mouse Mouse structure pointer.
* @param x_off X-coordinate offset.
* @param y_off Y-coordinate offset.
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_move_relative(mouse_t *mouse, int x_off, int y_off)
{
((void)mouse);
int ret;
INPUT input = {0};
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_MOVE;
input.mi.dx = x_off;
input.mi.dy = y_off;
ret = SendInput(1, &input, sizeof(input));
return (ret == 0);
}
/**
* @brief Makes a 'button press' event according to the
* @p mouse pointer and the @p button (left or right).
*
* @param mouse Mouse structure pointer.
* @param button Which button was pressed (either
* MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT).
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_down(mouse_t *mouse, int button)
{
((void)mouse);
int ret;
INPUT input = {0};
if (button == MOUSE_BTN_LEFT)
button = MOUSEEVENTF_LEFTDOWN;
else
button = MOUSEEVENTF_RIGHTDOWN;
input.type = INPUT_MOUSE;
input.mi.dwFlags = button;
ret = SendInput(1, &input, sizeof(input));
return (ret == 0);
}
/**
* @brief Makes a 'button release' event according to the
* @p mouse pointer and the @p button (left or right).
*
* @param mouse Mouse structure pointer.
* @param button Which button was released (either
* MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT).
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_up(mouse_t *mouse, int button)
{
((void)mouse);
int ret;
INPUT input = {0};
if (button == MOUSE_BTN_LEFT)
button = MOUSEEVENTF_LEFTUP;
else
button = MOUSEEVENTF_RIGHTUP;
input.type = INPUT_MOUSE;
input.mi.dwFlags = button;
ret = SendInput(1, &input, sizeof(input));
return (ret == 0);
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#if !defined(__linux__) && !defined(__FreeBSD__)
#error "Expected Linux or FreeBSD here!"
#endif
#include <X11/Xlib.h>
#ifdef HAVE_XDOTOOL
#include <xdo.h>
#else
#include <X11/extensions/XTest.h>
#endif
#include "vtouchpad.h"
/**
* @dir examples/vtouchpad
* @brief Touchpad example directory
*
* @file mouse_x11.c
* @brief Mouse X11 implementation.
*/
/**
* Note:
* Although the use of libxdo/xdotool seems unnecessary (since libxdo *also*
* uses X11's XTest extension), I believe it can still be interesting: the
* xdotool implementation may change in the future, as well as support new
* systems, so dealing with libxdo seems to be better than using X11 directly.
*
* Link:
* If libxdo present: -lX11 -lxdo
* Else : -lX11 -lXtst
*
*/
/**
* Mouse pointer, hold OS-dependent data structures.
*/
struct mouse
{
#ifdef HAVE_XDOTOOL
xdo_t *xdo;
#else
Display *dpy;
Window root;
#endif
};
/**
* @brief Allocates a new mouse data structure.
*
* @return Returns a new mouse_t pointer object.
*/
mouse_t *mouse_new(void)
{
struct mouse *mouse;
mouse = calloc(1, sizeof(struct mouse));
if (!mouse)
return (NULL);
#ifdef HAVE_XDOTOOL
mouse->xdo = xdo_new(NULL);
if (!mouse->xdo)
return mouse_free(mouse);
#else
mouse->dpy = XOpenDisplay(0);
if (!mouse->dpy)
return mouse_free(mouse);
mouse->root = XRootWindow(mouse->dpy, DefaultScreen(mouse->dpy));
#endif
return (mouse);
}
/**
* @brief Frees a previously allocated mouse_t pointer.
*
* @return Always NULL.
*/
void *mouse_free(mouse_t *mouse)
{
if (!mouse)
goto out;
#ifdef HAVE_XDOTOOL
if (mouse->xdo)
xdo_free(mouse->xdo);
#else
if (mouse->dpy)
XCloseDisplay(mouse->dpy);
#endif
free(mouse);
out:
return (NULL);
}
/**
* @brief Moves the mouse pointed by @p mouse to the
* offsets @p x_off and @p y_off.
*
* @param mouse Mouse structure pointer.
* @param x_off X-coordinate offset.
* @param y_off Y-coordinate offset.
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_move_relative(mouse_t *mouse, int x_off, int y_off)
{
#ifdef HAVE_XDOTOOL
return xdo_move_mouse_relative(mouse->xdo, x_off, y_off);
#else
int ret;
ret = XTestFakeRelativeMotionEvent(mouse->dpy, x_off, y_off, CurrentTime);
XFlush(mouse->dpy);
return (ret == 0);
#endif
}
/**
* @brief Makes a 'button press' event according to the
* @p mouse pointer and the @p button (left or right).
*
* @param mouse Mouse structure pointer.
* @param button Which button was pressed (either
* MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT).
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_down(mouse_t *mouse, int button)
{
#ifdef HAVE_XDOTOOL
return xdo_mouse_down(mouse->xdo, CURRENTWINDOW, button);
#else
int ret;
ret = XTestFakeButtonEvent(mouse->dpy, button, 1, CurrentTime);
XFlush(mouse->dpy);
return (ret == 0);
#endif
}
/**
* @brief Makes a 'button release' event according to the
* @p mouse pointer and the @p button (left or right).
*
* @param mouse Mouse structure pointer.
* @param button Which button was released (either
* MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT).
*
* @return Returns 0 if success, 1 otherwise.
*/
int mouse_up(mouse_t *mouse, int button)
{
#ifdef HAVE_XDOTOOL
return xdo_mouse_up(mouse->xdo, CURRENTWINDOW, button);
#else
int ret;
ret = XTestFakeButtonEvent(mouse->dpy, button, 0, CurrentTime);
XFlush(mouse->dpy);
return (ret == 0);
#endif
}

View File

@@ -0,0 +1,210 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ws.h>
#include "vtouchpad.h"
/**
* @dir examples/vtouchpad
* @brief Touchpad example directory
*
* @file vtouchpad.c
* @brief Main file.
*/
/* Mouse global structure. */
mouse_t *mouse;
/**
* @brief Given a string @p ev containing a single event,
* parses it as expected.
*
* The event can be (D stands to 'delimiter', currently ';'):
* (Mouse Movement): mouse_move D off_X D off_Y, where the offset is an
* integer type that represents the amount of pixels
* the mouse have to move. Example: mouse_move;10;-20.
*
* Negative offsets represents movement to the left and
* to the top. Positive offsets are the opposite.
*
* (Mouse Button Press): Represents if a mouse button has been pressed or
* released.
*
* Valid events are:
* a) mouse_btn_left_down (left button press)
* b) mouse_btn_left_up (left button release)
* c) mouse_btn_right_down
* d) mouse_btn_right_up
*
* @param ev String representing the event.
* @param error Set to 1 if an error was found, 0 otherwise.
*
* @returns Returns a mouse_event structure.
*/
struct mouse_event parse_event(const char *ev, int *error)
{
struct mouse_event mev = {0};
char str[128] = {0};
char *saveptr, *s;
*error = 1;
saveptr = NULL;
strncpy(str, ev, sizeof(str) - 1);
s = strtok_r(str, EVENT_DELIMITER, &saveptr);
if (!strcmp(s, "mouse_move"))
mev.event = MOUSE_MOVE;
else if (!strcmp(s, "mouse_btn_left_down"))
mev.event = MOUSE_BTN_LEFT_DOWN;
else if (!strcmp(s, "mouse_btn_left_up"))
mev.event = MOUSE_BTN_LEFT_UP;
else if (!strcmp(s, "mouse_btn_right_down"))
mev.event = MOUSE_BTN_RIGHT_DOWN;
else if (!strcmp(s, "mouse_btn_right_up"))
mev.event = MOUSE_BTN_RIGHT_UP;
else
{
fprintf(stderr, "Unknown event: (%s)\n", s);
goto out0;
}
if (mev.event != MOUSE_MOVE)
goto out1;
/* X offset. */
s = strtok_r(NULL, EVENT_DELIMITER, &saveptr);
if (!s)
{
fprintf(stderr, "A movement event requires a X/Y offsets, X not found!\n");
goto out0;
}
mev.x_off = atoi(s);
/* Y offset. */
s = strtok_r(NULL, EVENT_DELIMITER, &saveptr);
if (!s)
{
fprintf(stderr, "A movement event requires a X/Y offsets, Y not found!\n");
goto out0;
}
mev.y_off = atoi(s);
out1:
*error = 0;
out0:
return (mev);
}
/**
* @brief On open event, just signals that a new client has
* been connected.
*
* @param client Client connection.
*/
void onopen(ws_cli_conn_t *client) {
(void)client;
printf("Connected!\n");
}
/**
* @brief On close event, just signals that a client has
* been disconnected.
*
* @param client Client connection.
*/
void onclose(ws_cli_conn_t *client) {
(void)client;
printf("Disconnected!\n");
}
/**
* @brief For each new event, parses the string as expected all call
* the appropriate routines.
*
* @param client Client connection. (ignored)
*
* @param msg Received message/event.
*
* @param size Message size (in bytes). (ignored)
*
* @param type Message type. (ignored)
*/
void onmessage(ws_cli_conn_t *client,
const unsigned char *msg, uint64_t size, int type)
{
((void)client);
((void)size);
((void)type);
struct mouse_event mev;
int err;
/* Parse the event. */
mev = parse_event((const char *)msg, &err);
if (err)
return;
/* Call the appropriate routine. */
switch (mev.event)
{
case MOUSE_MOVE:
VTOUCH_DEBUG(("move: %d / %d\n", mev.x_off, mev.y_off));
mouse_move_relative(mouse, mev.x_off, mev.y_off);
break;
case MOUSE_BTN_LEFT_DOWN:
VTOUCH_DEBUG(("mouse left down\n"));
mouse_down(mouse, MOUSE_BTN_LEFT);
break;
case MOUSE_BTN_LEFT_UP:
VTOUCH_DEBUG(("mouse left up\n"));
mouse_up(mouse, MOUSE_BTN_LEFT);
break;
case MOUSE_BTN_RIGHT_DOWN:
VTOUCH_DEBUG(("mouse right down\n"));
mouse_down(mouse, MOUSE_BTN_RIGHT);
break;
case MOUSE_BTN_RIGHT_UP:
VTOUCH_DEBUG(("mouse right up\n"));
mouse_up(mouse, MOUSE_BTN_RIGHT);
break;
}
}
/* Main routine. */
int main(void)
{
struct ws_events evs;
/* Register events. */
evs.onopen = &onopen;
evs.onclose = &onclose;
evs.onmessage = &onmessage;
/* Mouse. */
mouse = mouse_new();
ws_socket(&evs, 8080, 0, 1000);
mouse_free(mouse);
return (0);
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef VTOUCHPAD_H
#define VTOUCHPAD_H
/* Debug. */
#ifndef DISABLE_VERBOSE
#define VTOUCH_DEBUG(x) printf x
#else
#define VTOUCH_DEBUG(x)
#endif
/* Mouse definitions. */
#define MOUSE_MOVE 1
#define MOUSE_BTN_LEFT_DOWN 2
#define MOUSE_BTN_LEFT_UP 4
#define MOUSE_BTN_RIGHT_DOWN 8
#define MOUSE_BTN_RIGHT_UP 16
#define EVENT_DELIMITER "; "
/* Mouse buttons. */
#define MOUSE_BTN_LEFT 1
#define MOUSE_BTN_RIGHT 3
/* Mouse event structure. */
struct mouse_event
{
int event;
int x_off;
int y_off;
};
/* Opaque type to 'struct mouse'. */
typedef struct mouse mouse_t;
/* External declarations. */
extern mouse_t *mouse_new(void);
extern void* mouse_free(mouse_t *mouse);
extern int mouse_move_relative(mouse_t *mouse, int x_off, int y_off);
extern int mouse_down(mouse_t *mouse, int button);
extern int mouse_up(mouse_t *mouse, int button);
#endif /* MAIN_H */

View File

@@ -0,0 +1,227 @@
<!--
Copyright (C) 2022 Davidson Francis <davidsondfgl@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
-->
<html>
<head>
<script>
let ws;
let x = 0;
let y = 0;
let move_counter = 0;
let is_connected = false;
const THRESHOLD = 1;
const DEBUG = true;
const DELIMITER = ";";
function get_touch(e) {
const evt = (typeof e.originalEvent === 'undefined') ?
e : e.originalEvent;
const touch = evt.touches[0] || evt.changedTouches[0];
return (touch);
}
function is_touch_device() {
return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
}
function fix_num(n) {
return (n == NaN ? 0 : Math.round(n));
}
window.addEventListener("load", function () {
/* Mouse events. */
const touch = document.getElementById('touch');
touch.addEventListener('mousemove', e => {
if (is_touch_device())
return;
mouse_move(e.offsetX, e.offsetY);
});
touch.addEventListener('mousedown', e => {
mouse_down(e);
});
touch.addEventListener('mouseup', e => {
mouse_up(e);
});
touch.addEventListener('mouseenter', e => {
x = e.offsetX;
y = e.offsetY;
});
/* Touch events. */
touch.addEventListener('touchmove', e => {
const touch = get_touch(e);
mouse_move(fix_num(touch.pageX), fix_num(touch.pageY));
});
touch.addEventListener('touchstart', e => {
const touch = get_touch(e);
x = fix_num(touch.pageX);
y = fix_num(touch.pageY);
});
/* Button left. */
const btn_left = document.getElementById('btn_left');
btn_left.addEventListener('mousedown', e => {
mouse_down(e, "mouse_btn_left_down");
});
btn_left.addEventListener('mouseup', e => {
mouse_up(e, "mouse_btn_left_up");
});
/* Button right. */
const btn_right = document.getElementById('btn_right');
btn_right.addEventListener('mousedown', e => {
mouse_down(e, "mouse_btn_right_down");
});
btn_right.addEventListener('mouseup', e => {
mouse_up(e, "mouse_btn_right_up");
});
/* Connect button. */
document.getElementById("bt_conn").onclick = function(){
const addr = document.getElementById("srv_addr").value;
toggle_connection(addr);
};
});
function toggle_connection(addr) {
if (is_connected) {
is_connected = false;
ws.close();
document.getElementById("bt_conn").value = "Disconnect!";
return;
}
ws = new WebSocket(addr);
ws.onopen = function(e) {
is_connected = true;
document.getElementById("bt_conn").value = "Disconnect!";
};
ws.onclose = function(e) {
is_connected = false;
document.getElementById("bt_conn").value = "Connect!";
};
}
function mouse_move(offX, offY) {
if (!is_connected)
return;
/* Do not trigger too much events.. we do not need
* to do 'micro movements' */
move_counter++;
if (move_counter != THRESHOLD)
return;
move_counter = 0;
const movX = offX - x;
const movY = offY - y;
x = offX;
y = offY;
ws.send("mouse_move" + DELIMITER + movX + DELIMITER + movY);
if (DEBUG)
console.log(movX + " / " + movY);
}
function mouse_down(e, btn) {
let event;
if (!is_connected)
return;
if (!btn) {
if (e.button == 0)
event = "mouse_btn_left_down";
else if (e.button == 2)
event = "mouse_btn_right_down";
else
return;
}
else
event = btn;
if (DEBUG)
console.log(event);
ws.send(event);
}
function mouse_up(e, btn) {
let event;
if (!is_connected)
return;
if (!btn) {
if (e.button == 0)
event = "mouse_btn_left_up";
else if (e.button == 2)
event = "mouse_btn_right_up";
else
return;
}
else
event = btn;
if (DEBUG)
console.log(event);
ws.send(event);
}
</script>
<style type="text/css">
body {
margin: 0 !important;
padding: 0 !important;
overscroll-behavior-y: contain;
}
#header { padding-top: 10px; padding-left: 5px; }
#touch {
background-image: linear-gradient(white, gray);
border: solid;
border-radius: 5px;
width: 80%;
height: 78%;
margin: 0 auto;
}
#wrapper { margin: 0 auto; width: 99%; text-align: center; }
#btn_left, #btn_right {
background-image: linear-gradient(white, gray);
border: solid;
border-radius: 5px;
width: 40%;
height: 11%;
display: inline-block;
margin-right: 10px;
margin-top: 10px;
}
#btn_right {
margin-right: 0;
}
</style>
</head>
<body oncontextmenu="return false;">
<div id="header">
Server: <input type="text" id="srv_addr" value="ws://192.168.X.Y:8080">
<input type="button" id="bt_conn" name="bt_conn" value="Connect!"><br /><br />
</div>
<div id="touch"></div>
<div id="wrapper">
<div id="btn_left"></div><div id="btn_right"></div>
</div>
</body>
</html>