diff --git a/.gitignore b/.gitignore index a547bf3..016b3e4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,11 +7,21 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* +# Node JS node_modules dist dist-ssr *.local +# Yarn 2 +.yarn/* +!.yarn/patches +!.yarn/releases +!.yarn/plugins +!.yarn/sdks +!.yarn/versions +.pnp.* + # Editor directories and files .vscode/* !.vscode/extensions.json @@ -22,3 +32,9 @@ dist-ssr *.njsproj *.sln *.sw? + +# Python +server/__pycache__ +server/build +server/dist +/build/* \ No newline at end of file diff --git a/build.js b/build.js new file mode 100644 index 0000000..3ae6011 --- /dev/null +++ b/build.js @@ -0,0 +1,22 @@ +const { execSync } = require('child_process'); +var fs = require('fs'); + + +console.log('Building Client') +console.log(run_cmd('yarn webbuild')) +// copyFile('./dist/index.html', './build/index.html') + +console.log('Building Server') +console.log(run_cmd('pyinstaller -F ./server.spec', path = "./server")) +copyFile('./server/dist/server.exe', './build/vTablet.exe') + + +console.log('Finished!') + +function run_cmd(cmd, path = './') { + return execSync(cmd,{stdio:[0,1,2], cwd: path}) +} + +function copyFile(src, dist) { + fs.writeFileSync(dist, fs.readFileSync(src)); +} \ No newline at end of file diff --git a/package.json b/package.json index 68a15cc..3311046 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,12 @@ { "name": "vtablet", "private": true, - "version": "0.0.0", + "version": "0.0.1", "scripts": { + "serve": "vite preview", + "webbuild": "vite build", "dev": "vite", - "build": "vite build", - "preview": "vite preview" + "build": "node ./build.js" }, "dependencies": { "@vueuse/core": "^8.1.2", diff --git a/server/server.py b/server/server.py new file mode 100644 index 0000000..dd16f2d --- /dev/null +++ b/server/server.py @@ -0,0 +1,112 @@ +# coding=utf-8 +from ctypes import CDLL, c_uint64, c_char, c_double, c_ushort, windll +import mouse +import json +import sys +import pyautogui +import os.path +from simple_http_server import WebsocketHandler, WebsocketRequest, WebsocketSession, websocket_handler, request_map +import simple_http_server.server as server + +DATA_PATH = os.path.split(os.path.realpath(__file__))[0] +CLIENT_FILE = os.path.join(DATA_PATH, "index.html") +HEIGHT, WIDTH = pyautogui.size() +SETTING_FILE = "settings.json" + +VMULTI_DLL = CDLL(os.path.join(DATA_PATH, 'vTabletDriverDll.dll')) + +class VMulti: + def __init__(self): + try: + VMULTI_DLL.vMulti_connect.restype = c_uint64 + self.controller = VMULTI_DLL.vMulti_connect() + print(self.controller) + except: + print('no vMulti devices') + pass + def update_digi(self, x, y, p, b): + if not self.is_connected(): + return False + VMULTI_DLL.vMulti_updateDigi(c_uint64(self.controller), c_ushort(x), c_ushort(y), c_double(p), c_char(b)) + def is_connected(self): + return VMULTI_DLL.vMulti_isOpened(c_uint64(self.controller)) + +vmulti = VMulti() + +def save_setting(data): + with open(SETTING_FILE, 'w') as f: + f.write(data) + print("Saved") + + +def load_setting(): + data = "" + if os.path.isfile(SETTING_FILE): + with open(SETTING_FILE) as f: + data = f.read() + return data + + +# Client +@request_map("/") +def frontend_ctroller_function(): + data = "" + if os.path.isfile(CLIENT_FILE): + with open(CLIENT_FILE, 'r', encoding='utf-8') as f: + data = f.read() + return data + + +# WS server +@websocket_handler(endpoint="/ws") +class WSHandler(WebsocketHandler): + + def on_handshake(self, request: WebsocketRequest): + return 0, {} + + def on_open(self, session: WebsocketSession): + print(">> Connected! ") + # print(session.request.path_values) + + def on_close(self, session: WebsocketSession, reason: str): + print(">> Closeed Connect::") + # print(reason) + + def on_text_message(self, session: WebsocketSession, message: str): + # print(">> Got text message: ") + # print(message) + + data = json.loads(message) + + if data["type"] == "move": + # print(float(data['x']) * HEIGHT, float(data['y']) * WIDTH) + mouse.move(float(data['x']) * HEIGHT, float(data['y']) * WIDTH) + + elif data["type"] == "click": + if data["action"] == "down": + mouse.press() + elif data["action"] == "up": + mouse.release() + + elif data["type"] == "digi": + x = data['x'] + y = data['y'] + pressure = data['pressure'] + bottom = data['bottom'] + print(x, y, pressure, bottom) + is_success = vmulti.update_digi(x, y, pressure, bottom) + + elif data["type"] == "save_setting": + save_setting(data["setting"]) + + elif data["type"] == "load_setting": + session.send(json.dumps({"setting": load_setting()})) + return + + +def main(*args): + server.start(port=8888) + + +if __name__ == "__main__": + main() diff --git a/server/server.spec b/server/server.spec new file mode 100644 index 0000000..04846a5 --- /dev/null +++ b/server/server.spec @@ -0,0 +1,40 @@ +# -*- mode: python ; coding: utf-8 -*- + + +block_cipher = None + + +a = Analysis(['server.py'], + pathex=[], + binaries=[], + datas=[("vTabletDriverDll.dll",'.'), ('../dist/index.html', '.')], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) + +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='server', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None ) diff --git a/server/vTabletDriverDll.dll b/server/vTabletDriverDll.dll new file mode 100644 index 0000000..ce3f075 Binary files /dev/null and b/server/vTabletDriverDll.dll differ diff --git a/src/components/Settings.vue b/src/components/Settings.vue index e9a55e2..90cd17a 100644 --- a/src/components/Settings.vue +++ b/src/components/Settings.vue @@ -3,7 +3,7 @@
-
-
+
+
- 大小 + 大小: {{settings.data.aria.size * 100}}% - x 偏移 + x 偏移: {{settings.data.aria.offset.x * 100}}% - y 偏移 + y 偏移: {{settings.data.aria.offset.y * 100}}% - 旋转 + 旋转: {{settings.data.aria.rotate}}° +
+
+ + 可以刷新页面重新显示设置! +
+
- +
+ +
+
+ + 仅支持 Windows, 需要驱动 +
+
+
+ + +
+ Version 0.0.1 by @Teages + +
+
@@ -90,29 +114,29 @@ const settings = useSettingStore() var ws = null -// onMounted(()=>{ +onMounted(()=>{ -// console.log("connecting:", `ws://${window.location.host}/ws`) -// ws = new WebSocket(`ws://${window.location.host}/ws`) + console.log("connecting:", `ws://${window.location.host}/ws`) + ws = new WebSocket(`ws://${window.location.host}/ws`) -// ws.addEventListener('open', ()=>{ -// loadSetting(ws) -// }) -// ws.addEventListener('message', (e)=>{ -// let cloudData = JSON.parse(e.data) -// console.log(e.data) -// try { -// let cloudSetting = JSON.parse(cloudData.setting) -// console.log(cloudSetting) -// if (!cloudSetting) throw "no cloud setting"; -// settings.loadSetting(cloudSetting) -// console.log("loaded from server") -// } catch (error) { -// saveSetting(ws) -// console.error(error) -// } -// }) -// }) + ws.addEventListener('open', ()=>{ + loadSetting(ws) + }) + ws.addEventListener('message', (e)=>{ + let cloudData = JSON.parse(e.data) + console.log(e.data) + try { + let cloudSetting = JSON.parse(cloudData.setting) + console.log(cloudSetting) + if (!cloudSetting) throw "no cloud setting"; + settings.loadSetting(cloudSetting) + console.log("loaded from server") + } catch (error) { + saveSetting(ws) + console.error(error) + } + }) +}) function loadSetting() { return ws.send(JSON.stringify({ diff --git a/src/components/VTabletCanvas.vue b/src/components/VTabletCanvas.vue index ef65e90..c09f246 100644 --- a/src/components/VTabletCanvas.vue +++ b/src/components/VTabletCanvas.vue @@ -57,7 +57,8 @@ const ariaCSS = computed(() => { return { width: `${ariaWidth.value}px`, height: `${ariaHeight.value}px`, - background: settings.data.theme.ariaBackground + background: settings.data.theme.ariaBackground, + opacity: (settings.data.theme.hideCAria && !settings.dialog) ? '0' : '1' } }) @@ -66,12 +67,24 @@ onMounted(() => { window.addEventListener('resize', () => resize) window.onresize = resize + console.log("connecting:", `ws://${window.location.host}/ws`) + let ws = new WebSocket(`ws://${window.location.host}/ws`) + ws.addEventListener('close', (e) => { + if (settings.data.autoReload) { + alert("连接丢失, 需要刷新.") + console.error("连接丢失, 需要刷新.") + location.reload(); + } + }) + + let box = document.getElementById('box') - box.addEventListener('pointermove', e => pointerEventHandle(e)) - box.addEventListener('pointerdown', e => pointerEventHandle(e)) - box.addEventListener('pointerup', e => pointerEventHandle(e)) + let aria = document.getElementById('aria') + box.addEventListener('pointermove', e => pointerEventHandle(e, ws)) + aria.addEventListener('pointerdown', e => pointerDown(e, ws)) + box.addEventListener('pointerup', e => pointerUp(e, ws)) }) -function pointerEventHandle(event) { +function pointerEventHandle(event, ws) { let box = document.getElementById('box') let aria = document.getElementById('aria') let ox = event.offsetX @@ -107,7 +120,51 @@ function pointerEventHandle(event) { x = newX + 0.5 y = newY + 0.5 } - console.log(x, y) + // console.log(x, y) + if (event.pointerType.toLowerCase() == 'pen' && settings.data.pressure == true) { + sendMsg({ + type: 'digi', + x: Math.round(x * 32767), + y: Math.round(y * 32767), + pressure: event.pressure, + bottom: event.pressure > 0 ? 0x21 : 0x20 + }, ws) + } else { + sendMsg({ + type: 'move', + x: x, + y: y + }, ws) + } + +} +function pointerDown(event, ws) { + if (event.pointerType.toLowerCase() == 'pen' && settings.data.pressure == true) { + return + } + if (settings.data.blockClick) { + return + } + sendMsg({ + type: 'click', + action: 'down' + }, ws) +} +function pointerUp(event, ws) { + if (event.pointerType.toLowerCase() == 'pen' && settings.data.pressure == true) { + return + } + sendMsg({ + type: 'click', + action: 'up' + }, ws) +} +function sendMsg(msgObj, ws) { + try { + ws.send(JSON.stringify(msgObj)) + } catch (error) { + console.error(error) + } } function resize() { screen.value.height = window.innerHeight diff --git a/src/stores/settings.js b/src/stores/settings.js index 7f66ada..b7ad054 100644 --- a/src/stores/settings.js +++ b/src/stores/settings.js @@ -42,6 +42,7 @@ function getDefaultSetting() { exitFullScreenBtn: true, settingBtn: true, blockClick: false, - pressure: false + pressure: false, + autoReload: true, } } \ No newline at end of file