diff --git a/README.md b/README.md
index ddc2ae4..fe0df4e 100644
--- a/README.md
+++ b/README.md
@@ -2,3 +2,9 @@
HamClock Remote Access - access HamClock remotely
This is currently in early development.
+
+## Client
+The client is in the `client` directory.
+
+## Server
+The server is in the `server` directory.
diff --git a/app.py b/app.py
deleted file mode 100644
index 09edec9..0000000
--- a/app.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from flask import Flask, request, Response
-import os
-import requests
-import base64
-#import hcapi
-app = Flask(__name__)
-
-@app.route('/client')
-def client():
- with open('client.html') as f:
- return f.read()
diff --git a/client.html b/client.html
deleted file mode 100644
index 3b32914..0000000
--- a/client.html
+++ /dev/null
@@ -1,125 +0,0 @@
-
-
-
- HamClock
-
-
-
-
-
-
HamClock Remote Access
-
-
-
-
-
-
diff --git a/client/client.css b/client/client.css
new file mode 100644
index 0000000..7347177
--- /dev/null
+++ b/client/client.css
@@ -0,0 +1,48 @@
+body {
+ background-color: black;
+ color: white;
+ font-family: sans-serif;
+ font-size: 16pt;
+}
+input {
+ background-color: #222222;
+ border: none;
+ color: white;
+ font-size: 16pt;
+ text-align: left !important;
+}
+button {
+ background-color: #00FF00;
+ border: none;
+ font-size: 16pt;
+}
+.loginline {
+ padding: 3px;
+}
+form {
+ float: left;
+}
+form * {
+ text-align: right;
+}
+#errorbox {
+ font-size: 200%;
+ text-align: center
+}
+#holder {
+ margin: 0;
+ padding: 0;
+ width: 100vw;
+ height: 100vh;
+ text-align: center
+}
+#holder * {
+ margin: 0 auto;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+#errorbox * {
+ position: static;
+}
diff --git a/client/client.html b/client/client.html
new file mode 100644
index 0000000..845b287
--- /dev/null
+++ b/client/client.html
@@ -0,0 +1,22 @@
+
+
+
+ HamClock
+
+
+
+
+
HamClock Remote Access
+
+
+
+
+
+
diff --git a/client/client.js b/client/client.js
new file mode 100644
index 0000000..adeae91
--- /dev/null
+++ b/client/client.js
@@ -0,0 +1,122 @@
+function resize() {
+ let bodyWidth = window.innerWidth
+ let maxWidth = Math.min(bodyWidth - 60, 800)
+ let bodyHeight = window.innerHeight
+ let maxHeight = Math.min(bodyHeight - 60, 480)
+ let equivalentHeight = maxWidth * 0.6
+ if (equivalentHeight >= maxHeight) {
+ displayHeight = Math.round(maxHeight)
+ displayWidth = Math.round(maxHeight * (5/3))
+ } else {
+ displayWidth = Math.round(maxWidth)
+ displayHeight = Math.round(maxWidth * 0.6)
+ }
+ canvas.style.width = displayWidth + 'px'
+ canvas.style.height = displayHeight + 'px'
+ errorbox.style.width = displayWidth + 'px'
+ errorbox.style.height = displayHeight + 'px'
+}
+
+
+function setup() {
+ ws = new WebSocket(localStorage.getItem('url'))
+ ws.onmessage = function(e) {
+ window.m = e
+ let type, pos, x, y, data, img, code, msg
+ type = e.data.split('%')[0]
+ if ( type == 'pic' ) {
+ pos = e.data.split('%')[1]
+ x = Number(pos.split('x')[0])
+ y = Number(pos.split('x')[1])
+ data = e.data.split('%')[2]
+ img = new Image()
+ img.src = data
+ img.addEventListener("load", function(){
+ ctx.drawImage(img,x,y)
+ })
+ errorbox.style.display = 'none'
+ canvas.style.display = 'block'
+ } else if ( type == 'err' ) {
+ code = e.data.split('%')[1]
+ msg = e.data.split('%')[2]
+ errorbox.textContent = msg
+ errorbox.style.display = 'block'
+ canvas.style.display = 'none'
+ console.error('Server reported error: ' + code + ': ' + msg)
+ if (code[0] == '*') {
+ errorbox.innerHTML = msg + '
Please refresh page'
+ ws.onclose = function(e){}
+ ws.onerror = function(e){}
+ }
+ } else if (type == 'ack') {
+ ws.send('ack')
+ }
+ }
+ ws.onerror = function(e){
+ errorbox.innerHTML = 'Connection lost
Please refresh page'
+ errorbox.style.display = 'block'
+ canvas.style.display = 'none'
+ }
+ ws.onclose = function(e){
+ errorbox.innerHTML = 'Connection lost
Please refresh page'
+ errorbox.style.display = 'block'
+ canvas.style.display = 'none'
+ }
+ setInterval(function(){ws.send('ack')}, 3000)
+}
+
+
+function touch(e) {
+ click_timeout = setTimeout(function() {
+ is_short = false
+ }, 1000)
+}
+
+
+function release(e) {
+ clearTimeout(click_timeout)
+ x = e.layerX
+ y = e.layerY
+ w = displayWidth
+ length = is_short?0:1
+ ws.send('touch ' + localStorage.getItem('password') + ' ' + x + ' ' + y + ' ' + w + ' ' + length)
+}
+
+
+function connect(e) {
+ e.preventDefault()
+ holder.style.display = 'block'
+ login.style.display = 'none'
+ localStorage.setItem('url', url_el.value)
+ localStorage.setItem('password', password_el.value)
+ setup()
+}
+
+
+let canvas = document.querySelector('canvas')
+let errorbox = document.querySelector('#errorbox')
+
+let login = document.querySelector('#login')
+let holder = document.querySelector('#holder')
+
+let url_el = document.querySelector('#url')
+let password_el = document.querySelector('#password')
+let start_button = document.querySelector('#start')
+
+let is_short = true
+let click_timeout
+
+let ctx = canvas.getContext("2d")
+
+let displayWidth, displayHeight
+
+let ws
+
+
+resize()
+window.onresize = resize
+
+canvas.onmousedown = touch
+canvas.onmouseup = release
+
+start.onclick = connect
diff --git a/crops.json b/server/crops.json
similarity index 100%
rename from crops.json
rename to server/crops.json
diff --git a/server/hcapi.py b/server/hcapi.py
new file mode 100644
index 0000000..a4a5a9b
--- /dev/null
+++ b/server/hcapi.py
@@ -0,0 +1,41 @@
+import os
+import requests
+import threading
+import json
+import time
+
+def get_img():
+ os.system('xwd -root -silent | convert xwd:- img.bmp')
+
+ with open('crops.json') as f:
+ crops = json.load(f)
+
+ threads = []
+ for crop in crops:
+ threads.append(threading.Thread(target=os.system, args=(f'convert img.bmp -crop {crop} pieces/{crop.split("+", 1)[1].replace("+", "x")}.jpg',)))
+ threads[-1].start()
+ for thread in threads:
+ thread.join()
+
+ os.system("md5sum -c oldlist 2>/dev/null | grep FAILED > newlist")
+ changed = []
+ with open('newlist') as f:
+ lines = f.readlines()
+ for line in lines:
+ changed.append(line.split(": ")[0].split('.')[0].split('/')[1])
+ os.system("md5sum pieces/* > oldlist")
+ return changed
+
+def get_full_img():
+ os.system('xwd -root -silent | convert xwd:- full.jpg')
+ return 'full.jpg'
+
+def touch(x, y, w, is_long):
+ x = round(800 * x / w)
+ y = round(800 * y / w)
+ if is_long:
+ os.system(f'xdotool mousemove {x} {y} mousedown 1')
+ time.sleep(2)
+ os.system(f'xdotool mouseup 1')
+ else:
+ os.system(f'xdotool mousemove {x} {y} click 1')
diff --git a/hcapi.py b/server/ohcapi.py
similarity index 88%
rename from hcapi.py
rename to server/ohcapi.py
index f1c89e8..54e3edb 100644
--- a/hcapi.py
+++ b/server/ohcapi.py
@@ -11,11 +11,6 @@ def get_img():
os.system('convert full_img.bmp -resize 800x480 img.bmp')
- #crops = []
- #for n in range(10):
- # for m in range(6):
- # crops.append(f'80x80+{n*80}+{m*80}')
- #print(crops)
with open('crops.json') as f:
crops = json.load(f)
@@ -47,7 +42,7 @@ def get_full_img():
return 'full.jpg'
-def touch(x, y, w):
+def touch(x, y, w, is_long):
x = round(800 * x / w)
y = round(800 * y / w)
- requests.get(f'http://localhost:8080/set_touch?x={x}&y={y}&hold={0}')
+ requests.get(f'http://localhost:8080/set_touch?x={x}&y={y}&hold={1 if is_long else 0}')
diff --git a/wss.py b/server/wss.py
similarity index 66%
rename from wss.py
rename to server/wss.py
index c024e1c..f981c07 100644
--- a/wss.py
+++ b/server/wss.py
@@ -33,21 +33,33 @@ class Client:
self.server = server
self.queue = queue.Queue()
self.items = {}
+ self.lock = threading.Lock()
self.good = True
def send(self, item, name):
- self.items[name] = item
+ with self.lock:
+ self.items[name] = item
def ack(self):
- self.server.send_message(self.client, 'ack')
- self.good = False
+ with self.lock:
+ try:
+ self.server.send_message(self.client, 'ack')
+ self.good = False
+ except BrokenPipeError:
+ pass
def cycle(self):
- while not self.good:
- pass
- for name, item in self.items.items():
- self.server.send_message(self.client, item)
- del self.items[name]
+ with self.lock:
+ try:
+ while not self.good:
+ pass
+ for name, item in list(self.items.items()):
+ if not self.good:
+ break
+ self.server.send_message(self.client, item)
+ del self.items[name]
+ except BrokenPipeError:
+ pass
def run(self):
while True:
@@ -55,9 +67,7 @@ class Client:
clients = {}
def do_img(imgname):
- #server.send_message_to_all(img(imgname))
for client in clients.values():
- print(client)
client.send(img(imgname), imgname)
@@ -70,6 +80,12 @@ def img(imgname):
return response
def new_client(client, server):
+ if clients:
+ server.send_message(client, 'err%*inuse%Server already in use')
+ client['handler'].send_text("", opcode=0x8)
+ return
+
+ clients[client['id']] = Client(client, server)
try:
imgname = hcapi.get_full_img()
except Exception as e:
@@ -91,17 +107,24 @@ def do_touch(client, server, message):
if action == 'ack':
clients[client['id']].good = True
else:
- _, password, x, y, w = message.split(' ')
+ _, password, x, y, w, is_long = message.split(' ')
if password == 'password':
- x, y, w = int(x), int(y), int(w)
- hcapi.touch(x, y, w)
+ x, y, w, is_long = int(x), int(y), int(w), bool(is_long)
+ hcapi.touch(x, y, w, is_long)
else:
clients[client['id']].send(f'badpass', 'BADPASS')
+def client_left(client, server):
+ clients[client['id']].good = False
+ del clients[client['id']]
+
def do_cycles():
while True:
- cycle()
- time.sleep(0.2)
+ if clients:
+ cycle()
+ time.sleep(0.4)
+ else:
+ time.sleep(1)
tmp = tempfile.mkdtemp(prefix="HCRA-")
try:
@@ -112,6 +135,7 @@ try:
threading.Thread(target=do_cycles).start()
server.set_fn_new_client(new_client)
server.set_fn_message_received(do_touch)
+ server.set_fn_client_left(client_left)
server.run_forever()
finally:
os.chdir('/')