This commit is contained in:
bangae1
2025-10-22 14:56:52 +09:00
parent 1847f25fa3
commit e5aa5d1d5c
2 changed files with 57 additions and 44 deletions

View File

@@ -2,6 +2,7 @@
let peerConnection = null; let peerConnection = null;
let currentStream = null; let currentStream = null;
let dataChannel = null;
let connection = false; let connection = false;
// 메인 프로세스로부터 명령 수신 (IPC) // 메인 프로세스로부터 명령 수신 (IPC)
console.log('electronAPI:', window.electronAPI); console.log('electronAPI:', window.electronAPI);
@@ -53,6 +54,16 @@ window.electronAPI.receive('start-webrtc', async (data) => {
window.electronAPI.send('icecandidate', {type: 'icecandidate', targetId: data.targetId, server:'agent', 'candidate': event.candidate}); window.electronAPI.send('icecandidate', {type: 'icecandidate', targetId: data.targetId, server:'agent', 'candidate': event.candidate});
}, 2000) }, 2000)
} }
peerConnection.ondatachannel = (event) => {
console.log('datachannel', event);
dataChannel = event.channel;
dataChannel.onmessage = (event) => {
window.electronAPI.send('inputEvent', JSON.parse(event.data))
}
}
}); });
window.electronAPI.receive('displays', async (data) => { window.electronAPI.receive('displays', async (data) => {
@@ -66,5 +77,10 @@ window.electronAPI.receive('icecandidate', async (data) => {
} }
}); });
window.electronAPI.receive('disconnect', async (data) => {
peerConnection.close();
peerConnection = null;
});
// desktopCapturer 사용을 위해 preload 필요 // desktopCapturer 사용을 위해 preload 필요
// → 다음 단계에서 preload.js 설정 // → 다음 단계에서 preload.js 설정

85
main.js
View File

@@ -3,17 +3,16 @@ const { app, BrowserWindow, Tray, Menu, nativeImage, dialog, ipcMain } = require
const path = require('path'); const path = require('path');
const io = require('socket.io-client'); const io = require('socket.io-client');
const { desktopCapturer } = require('electron'); const { desktopCapturer } = require('electron');
const { mouse, Point, straightTo, keyboard, Key, clipboard } = require('@nut-tree-fork/nut-js') const { mouse, Point, straightTo, keyboard, Key, clipboard, Button} = require('@nut-tree-fork/nut-js')
// 고유 직원 ID // 고유 직원 ID
const EMPLOYEE_ID = "psn14020"; const EMPLOYEE_ID = "psn14020";
// 시그널링 서버 주소 // 시그널링 서버 주소
const SIGNALING_SERVER = 'http://localhost:3001'; const SIGNALING_SERVER = 'http://192.168.0.148:3001';
let tray = null; let tray = null;
let mainWindow = null; let mainWindow = null;
let socket = null; let socket = null;
let peerConnection = null;
let displayId = null; let displayId = null;
let offer = null; let offer = null;
let sources = null; let sources = null;
@@ -69,31 +68,44 @@ async function sendAvailableDisplays() {
console.error('디스플레이 목록 가져오기 실패:', err); console.error('디스플레이 목록 가져오기 실패:', err);
} }
} }
const keyMap = {
Enter: Key.Enter,
Backspace: Key.Backspace,
Tab: Key.Tab,
Escape: Key.Escape,
ArrowDown: Key.Down,
ArrowUp: Key.Up,
ArrowLeft: Key.Left,
ArrowRight: Key.Right,
}
// 🖱️ 입력 이벤트 처리 (robotjs 필요) // 🖱️ 입력 이벤트 처리 (robotjs 필요)
function setupInputHandler() { async function setupInputHandler(data) {
// robotjs는 별도 설치 및 권한 필요 // robotjs는 별도 설치 및 권한 필요
socket.on('inputEvent', async({ type, ...data }) => { if (data.type === 'mouse') {
try { const { x, y, action } = data;
if (type === 'mouse') { if(x === null || y === null) return
const { x, y, action } = data; const targetPoint = new Point(x, y)
const targetPoint = new Point(x, y) await mouse.move(straightTo(targetPoint));
await mouse.move(straightTo(targetPoint)); console.log(x, y , action)
if (action === 'click') { if (action === 'click') {
await mouse.leftClick(); await mouse.click(Button.LEFT)
}
} else if (type === 'keyboard') {
const { key } = data;
// 간단한 키 매핑 (보안상 복잡한 키는 제한 권장)
if (key.length === 1 || ['Enter', 'Backspace', 'Tab', 'Escape'].includes(key)) {
await keyboard.pressKey(key.toLowerCase());
}
}
} catch (err) {
console.error('입력 이벤트 처리 오류:', err);
} }
}); } else if (data.type === 'keyboard') {
const { key } = data;
// 간단한 키 매핑 (보안상 복잡한 키는 제한 권장)
console.log(key)
if(['Enter', 'Backspace', 'Tab', 'Escape'
, 'ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight'].includes(key)) {
await keyboard.pressKey(keyMap[key]);
await keyboard.releaseKey(keyMap[key]);
} else if(key.length === 1) {
await keyboard.type(key.toLowerCase());
}
// if (key.length === 1 || ['Enter', 'Backspace', 'Tab', 'Escape'].includes(key)) {
// await keyboard.type(key.toLowerCase());
// }
}
} }
// 시그널링 연결 // 시그널링 연결
@@ -120,10 +132,7 @@ function connectToSignaling() {
socket.on('disconnect', () => { socket.on('disconnect', () => {
console.log('시그널링 서버와 연결 끊김'); console.log('시그널링 서버와 연결 끊김');
if (peerConnection) { rendererWindow.webContents.send('disconnect');
peerConnection.close();
peerConnection = null;
}
}); });
socket.on('responseOffer', async (data) => { socket.on('responseOffer', async (data) => {
@@ -175,22 +184,6 @@ app.on('window-all-closed', () => {
// Windows/Linux에서 종료하지 않음 // Windows/Linux에서 종료하지 않음
}); });
ipcMain.on('input-event-from-renderer', (event, data) => {
// 여기서 robotjs로 입력 실행 (메인 프로세스에서만 가능)
try {
const robot = require('robotjs');
if (data.type === 'mouse') {
robot.moveMouse(Math.round(data.x), Math.round(data.y));
if (data.action === 'click') robot.mouseClick();
} else if (data.type === 'keyboard') {
robot.keyTap(data.key);
}
} catch (e) {
console.error('robotjs 오류:', e);
}
})
ipcMain.on('requestAnswer', (event, data) => { ipcMain.on('requestAnswer', (event, data) => {
socket.emit('requestAnswer', data); socket.emit('requestAnswer', data);
}) })
@@ -204,3 +197,7 @@ ipcMain.on('start', (event, data) => {
console.log('start', data); console.log('start', data);
socket.emit('start', data); socket.emit('start', data);
}) })
ipcMain.on('inputEvent', (event, data) => {
setupInputHandler(data)
})