
    :j2                      U d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlZddlZddlZddlmZ ddlmZmZ ddlmZ ddlmZmZ ddlZddlZ ee      j=                         j>                  j>                  Z  e!e       ejD                  vr"ejD                  jG                  d e!e              e dz  d	z  d
z  Z$	 ddl%m&Z& ddl(Z)ddl(m*Z* ddl+Z+ddl,m-Z-m.Z. ddl/Z/ddl0Z0ddl1m2Z2 ddl3m4Z4 ddl5m6Z6m7Z7 ddl8m9Z9 ddl:m;Z; ddl<m=Z= ddl>m?Z? ddl@mAZAmBZBmCZCmDZD ddlEmFZFmGZGmHZHmIZImJZJ ddlKmLZL 	 ddlMmNZN ddlOmPZP ddlQmRZR dZSdaUdeVd<    ej                         ZXdZYdZZd1dZ[d2d3d Z\d4d!Z]d5d"Z^d6d7d#Z_ G d$ d%      Z` G d& d'      Za G d( d)      Zb G d* d+      Zc G d, d-      Zdd8d.Zed8d/Zfegd0k(  r eh ef             y# e'$ r dZ&Y w xY w# e'$ rZTdZNdZPdZReTZSY dZT[TdZT[Tww xY w)9up  
yjm_win2rtc_capture.py

현재 단계:
    WebSocket JPEG preview + LAN/WAN 공유 준비 + 캡처 대상 선택 확장

지원 캡처 대상:
    1) 선택 창 HWND
    2) 디스플레이 0, 1, ...
    3) 전체 바탕화면

주의:
    아직 WebRTC가 아니다. WebRTC/aiortc로 넘어가기 전, 캡처·프레임·포트·공유 URL을 검증하는 단계다.
    )annotationsN)partial)SimpleHTTPRequestHandlerThreadingHTTPServer)Path)parse_qsurlparseSharedassetszig_styellow_tray.ico)WindowsTrayIcon)ttk)Image	ImageDraw)CaptureSourceManager)set_dpi_awareness)UpnpPortMappermake_viewer_url)RemoteInputController)RemoteActivityOverlay)make_file_transfer_handler)ClipboardSyncManager)MSG_CLIPBOARD_GETMSG_HOST_OPTION_SETMSG_HOST_OPTION_ACKmake_msg)AppSettingsSnapshotSettings
UiSettingsload_settingssave_settings)SnapshotRingWorker)serve)Headers)Response
int | Noneselected_hwndz	2.3.8.324v324c                <    | j                         j                  dz  S )uR   RTC/Auto8700 공통 인증 토큰 파일. 배포 ZIP에는 포함하지 않는다.z	_St.token)resolveparent)base_dirs    *D:\_St\RTC8790_8791\yjm_win2rtc_capture.pyst_token_file_pathr-   X   s    $${22    c                D   g }	 | j                         r| j                  dd      j                         D ]  }|j                         }|r|j	                  d      r'|j                  dd      }|r|d   ndj                         }t        |      dkD  r|d   ndj                         }t        |      d	k\  s|j                  ||d
        |s||rz	 | j                  j                  dd       t        j                  d      }d}
|
| dz  }
| j                  |
d       |j                  |dd
       |r|j                  d|        |S |S # t        $ r}	|r|j                  d|	       Y d}	~	d}	~	ww xY w# t        $ r}	|r|j                  d|	       Y d}	~	|S d}	~	ww xY w)uY   SSH authorized_keys처럼 한 줄에 토큰 1개 + 선택 이름/comment를 허용한다.utf-8ignore)encodingerrors#N   r       )tokencommentu   _St.token 읽기 실패: %sTparentsexist_ok    zP# _St shared tokens: one token per line, optional name/comment after whitespace
z rtc-auto8700-default
r2   zrtc-auto8700-defaultu   _St.token 자동 생성: %su   _St.token 생성 실패: %s)exists	read_text
splitlinesstrip
startswithsplitlenappend	Exceptionwarningr*   mkdirsecretstoken_urlsafe
write_textinfo)pathcreateloggerentrieslinerawpartstoken_valuer9   exctexts              r,   load_st_token_entriesrX   ]   s   G?;;=ITTVjjlcnnS1		$*+0uQxb??A'*5zA~582DDF{#r)NN[W#MN W v	CKKdT:!//3KfD{m#:;;DOOD7O3NN[=STU94@ N7N#  ?NN8#>?  	C<cBN	Cs7   B2E 7E A7E7 	E4E//E47	F FFc                    g }| xs g D ]F  }t        |xs i j                  d      xs d      j                         }|s6|j                  |       H |S )Nr8   r6   )strgetrB   rF   )rQ   valsenttoks       r,   st_token_valuesr_      sR    D}"}39"//'*0b1779KK  Kr.   c                    t        | xs d      j                         } | sy|xs g D ]  }|st        j                  | |      s y y)Nr6   FT)rZ   rB   rJ   compare_digest)gotallowed_tokensr^   s      r,   st_token_allowedrd      sJ    
ciR.


 C##7))#s3 $ r.   c                   | xs d}t        t        |      t        |      t        |      z         D ]  }	 t        j                  t        j                  t        j                        5 }|j                  t        j                  t        j                  d       |j                  ||f       ddd       |c S  t        d| d||z   dz
         # 1 sw Y   %xY w# t        $ r Y w xY w)u>   preferred_port가 사용 중이면 다음 포트를 찾는다.0.0.0.0r5   Nu3   사용 가능한 포트를 찾지 못했습니다: ~)
rangeintsocketAF_INETSOCK_STREAM
setsockopt
SOL_SOCKETSO_REUSEADDRbindOSError)hostpreferred_portmax_try	bind_hostportss         r,   find_free_portrx      s    !	Ic.)3~+>W+MN	v~~v/A/ABaV..0C0CQG	4() C K O GGWWXYgjqYqtuYuXvw
xx CB  		s*   2C'AC*	CC	C	C&%C&c                  N    e Zd ZdZddZd ZddZddZddZd Z	d	 Z
d
 Zd Zy)WindowOverlayuU   마우스가 멈춘 foreground HWND를 선택하고 빨간 테두리를 표시한다.Nc                    || _         || _        || _        |xs d | _        |xs d | _        d | _        d | _        d| _        d | _        d | _	        d| _
        d| _        y )Nc                      yNF r~   r.   r,   <lambda>z(WindowOverlay.__init__.<locals>.<lambda>   s    r.   c                      yNTr~   r~   r.   r,   r   z(WindowOverlay.__init__.<locals>.<lambda>   s    4r.   Tr      )rootrP   on_hwnd_change_callbackis_locked_callbackis_window_mode_callbackoverlay	last_rectrunningthreadlast_mouse_poslast_mouse_move_timemouse_stationary_threshold)selfr   rP   r   r   r   s         r,   __init__zWindowOverlay.__init__   sf    	'>$"4"G'>'P<$"$%!*+'r.   c                   | j                   r| j                   j                         s\t        j                  | j                  d      | _         | j                   j                          | j                  j                  d       y y )NTtargetdaemonzStarted HWND monitoring thread)r   is_alive	threadingThreadmonitor_foreground_windowstartrP   debugr   s    r,   start_monitoringzWindowOverlay.start_monitoring   s[    {{$++"6"6"8#**$2P2PY]^DKKKKK>? #9r.   c                T    	 t        | j                               S # t        $ r Y yw xY wr}   )boolr   rG   r   s    r,   
_is_lockedzWindowOverlay._is_locked   s-    	//122 		    	''c                T    	 t        | j                               S # t        $ r Y yw xY wr   )r   r   rG   r   s    r,   _is_window_modezWindowOverlay._is_window_mode   s-    	44677 		r   c                    |dk(  ry	 || j                   j                         k(  ryt        j                  |      xs d}|j	                  d      ryd|v rd|v ryy# t
        $ r Y yw xY w)Nr   Tr6   yjm_win2rtczviewer.htmlF)r   winfo_idwin32guiGetWindowTextrC   rG   r   hwndtitles      r,   _is_own_windowzWindowOverlay._is_own_window   s}    19		tyy))++**406BE.%-5*@   		s   A *A A 	A'&A'c                B   | j                   r?	 | j                         s&| j                          t        j                  d       Dt        j                         }t        j                         }| j                  r| j                  |k7  r|| _        || _        || j                  z
  | j                  k\  rvt        j                         }t        5  t        }| j                         rr|rpt        j                  |      r't        j                  |      }| j!                  |       nd a| j#                  d d        t        j                  d       	 d d d        U|t        k7  r| j%                  |      sn|at        j                  |      }t        j&                  |      }| j!                  |       | j#                  ||       | j(                  j+                  d|||       nCt        r=t        j                  t              s$d a| j                          | j#                  d d        d d d        t        j                  d       | j                   r>y y # 1 sw Y   .xY w# t,        $ r;}| j(                  j/                  d|       t        j                  d       Y d }~[d }~ww xY w)N皙?皙?z!Selected HWND=%s title=%s rect=%szHWND monitoring error: %sr5   )r   r   hide_overlaytimesleepr   GetCursorPosr   r   r   GetForegroundWindow	hwnd_lockr&   r   IsWindowGetWindowRectcreate_overlayr   r   r   rP   r   rG   error)r   current_poscurrent_timer   current_selectedrectr   rV   s           r,   r   z'WindowOverlay.monitor_foreground_window   s   ll+++-%%'JJsO&335#yy{&&4+>+>++M0<D-&1#$";";;t?^?^^#779D"+8(??,1A'001AB'/'='=>N'O $ 3 3D 904 $ < <T4 H JJsO$ #  =09L9LT9R,0M#+#9#9$#?D$,$:$:4$@E //5 88uE KK--.QSWY^`de*83D3D]3S,0M --/ 88tD1 #2 

3S ll  #4  !!"=sC

1sD   5I BI A?II CI!I II 	J#1JJc           	        | j                         s| j                          y |\  }}}}||z
  }||z
  }|dk  s|dk  ry 	 | j                  st        j                  | j
                        | _        | j                  j                  d       | j                  j                  dd       | j                  j                  dd       t        j                  | j                  dd      | _	        | j                  j                  t        j                  d       || j                  k7  r| j                  j                  | d| d	| d	|        | j                  j                  ||
       | j                  j                  d       | j                  j!                  dd|dz
  |dz
  dd       || _        y y # t"        $ r&}| j$                  j'                  d|       Y d }~y d }~ww xY w)Nr   T-topmostz-transparentcolorwhite)bghighlightthicknessfillexpandx+widthheightallr   red   )outliner   z!Overlay creation/update error: %s)r   r   r   tkToplevelr   overrideredirect
attributesCanvascanvaspackBOTHr   geometryconfigdeletecreate_rectanglerG   rP   r   )	r   r   r   yx1y1r   r   rV   s	            r,   r   zWindowOverlay.create_overlay  s   ##%1b"QaA:1	H<<!{{4995--d3''
D9''(;WE iiUVW  bggd ;t~~%%%q!AaS&AB""v">""5),,Q519fqjRW_`,a!% &  	HKKA3GG	Hs   E0F1 1	G :GG c                    | j                   r*	 | j                   j                          d | _         d | _        y y # t        $ r Y w xY wN)r   destroyrG   r   r   s    r,   r   zWindowOverlay.hide_overlay  sG    <<$$&  DL!DN   s   8 	AAc                2    d| _         | j                          y r}   )r   r   r   s    r,   stopzWindowOverlay.stop(  s    r.   )NNreturnr   )r   ri   r   r   )__name__
__module____qualname____doc__r   r   r   r   r   r   r   r   r   r~   r.   r,   rz   rz      s5    _,@.`H:"r.   rz   c                  L    e Zd ZdZddZd Zd Zd Zd Zd Z	dd	Z
dd
Zd Zy)WsFrameServeru   캡처 스레드에서 들어온 JPEG bytes를 모든 viewer.html 클라이언트에 보낸다.

    viewer.html에서 들어오는 텍스트 JSON은 원격 제어 이벤트로 처리할 수 있다.
    Nc                :   || _         t        |      | _        t        |      | _        || _        |xs t        j                  t              | _        || _	        d | _
        d | _        t               | _        d| _        t        j                          | _        || _        y r}   )rr   ri   requested_portrv   r8   logging	getLoggerr   rP   	auto_portloopr   setclientsr   r   Eventready_eventcontrol_handler)r   rr   rv   r8   rP   r   r   s          r,   r   zWsFrameServer.__init__3  sz    	!$iI	
; 1 1( ;"	u$??,.r.   c           	        
K   t        j                  d      
 j                  j                  
        j                  j                  dt         j                               
fd} fd}t        j                   |             }t        j                   |             }	 t        j                  ||ht         j                         d {   \  }}|D ]  }|j                         }	|	s|	 	 ||fD ]  }|j                           	  j                  r' j                  t        j                   dd	d
              j                  j#                  
        j                  j                  dt         j                               y 7 # t        $ r&}	 j                  j                  d|	       Y d }	~	d }	~	ww xY w# t        $ r&}	 j                  j                  d|	       Y d }	~	d }	~	ww xY w# ||fD ]  }|j                           	  j                  r' j                  t        j                   dd	d
             n2# t        $ r&}	 j                  j                  d|	       Y d }	~	nd }	~	ww xY w j                  j#                  
        j                  j                  dt         j                               w xY ww)Nr5   )maxsizezviewer connected: clients=%dc                 z   K   	 j                          d {   } j                  |        d {    27 7 wr   )r[   send)framequeue	websockets    r,   send_framesz*WsFrameServer.handler.<locals>.send_framesF  s6     #iik)nnU+++ )+s   ;7;9;;c            	     n  K   2 3 d {   } t        | t              sj                  s'j                  |       }|r5t        |dd      r(j                  j                  dt        |dd             o|srt        |dd      dk7  sj                  j                  dt        |dd             7 6 y w)NokFzcontrol ok: %smessager6   ignoredzcontrol blocked: %s)
isinstancerZ   r   getattrrP   r   )r   resultr   r   s     r,   receive_controlz.WsFrameServer.handler.<locals>.receive_controlK  s     !* aggs+0D0D!11':F'&$">))*:GFIWY<Z[GFIr$Bi$O))*?QZ\^A_`as<   B5B3B1B3B5B5A
B58B5	(B51B33B5)return_whenzviewer disconnected: %scontrolrelease_all)typeactionz(viewer disconnect release_all failed: %szviewer removed: clients=%d)asyncioQueuer   addrP   rM   rE   create_taskwaitFIRST_EXCEPTION	exceptionrG   r   cancelr   jsondumpsdiscard)r   r   r   r  	send_task	recv_taskdonependingtaskrV   r   s   ``        @r,   handlerzWsFrameServer.handlerA  s7    a(7T\\9JK	,
	a ''6	''(9:		N"),,I&#33# MD' nn&I  #I. /S''((YR_4`)ab LL  'KK93t||;LM'  	>KK7==	>  S!!"LcRRS #I. /S''((YR_4`)ab S!!"LcRRSLL  'KK93t||;LMs   B!K'*F FF 1F 4G3 5K3G AKF 	F>F94G3 9F>>G3 	G0
G+&K+G00K3J?3IJ?	I1I,'J?,I11AJ??Kc                t   | j                   sy 	 t        |j                        }t        |j                        j                  ddg      d   }t        j                  || j                         ry 	 t        t        y d}t        ddt        ddt        t        |            fg      |      S # t        $ r Y Gw xY w)	Nr8   r6   r   s   Forbidden: invalid token
  	Forbidden)Content-Typez
text/plainContent-Length)r8   r	   rN   r   queryr[   rJ   ra   rG   r$   r#   rZ   rE   )r   
connectionrequestparsedr8   bodys         r,   _check_token_process_requestz*WsFrameServer._check_token_process_requestl  s    zz	gll+FV\\*..w=a@E%%eTZZ8 9
 w,36FCPTI5WXY	
 	
  		s   AB+ +	B76B7c           
     J  K   t         t        dt               | j                  r%t	        | j
                  | j                        | _        t        | j                  | j
                  | j                  d d dd| j                        4 d {    | j                  j                  d| j
                  | j                         | j                  j                          t        j                          d {    d d d       d {    y 7 |7 7 	# 1 d {  7  sw Y   y xY ww)Nu   websockets import 실패:    )max_sizecompressionping_intervalping_timeoutprocess_requestzWebSocket server ws://%s:%s)r"   RuntimeError_WEBSOCKETS_IMPORT_ERRORr   rx   rr   r   rv   r  r$  rP   rM   r   r   r	  Futurer   s    r,   _runzWsFrameServer._run  s     =!;<T;UVWW>>&tyy$2E2EFDILLIIII ==	
 	
 KK:DIItyyQ  ".."""	
 	
 	
 #	
 	
 	
 	
s[   B	D#DD#A#D2D
3D7D#DD#
DD#D DD D#c                    | j                   ry d| _         | j                  j                          t        j                  | j
                  d      | _        | j                  j                          y )NTr   )r   r   clearr   r   _thread_mainr   r   r   s    r,   r   zWsFrameServer.start  sO    << &&d.?.?Mr.   c                   t        j                         | _        t        j                  | j                         	 | j                  j	                  | j                                d| _	        | j                  j                          y # t        $ r&}| j                  j                  d|       Y d }~Ld }~ww xY w# d| _	        | j                  j                          w xY w)NzWebSocket server error: %sF)r	  new_event_loopr   set_event_looprun_until_completer/  rG   rP   r   r   r   r   r   rV   s     r,   r2  zWsFrameServer._thread_main  s    **,	tyy)	#II((5 !DL  "	  	AKK:C@@	A !DL  "s)   )B 	B4B/*B7 /B44B7 7#Cc                    | j                   r| j                  sy | j                   j                  | j                  |       y r   )r   r   call_soon_threadsafe_push_message_in_loop)r   frame_bytess     r,   
push_framezWsFrameServer.push_frame  s,    yy		&&t'A'A;Or.   c                    | j                   r| j                  sy | j                   j                  | j                  t	        |             y r   )r   r   r9  r:  rZ   r   rW   s     r,   	push_textzWsFrameServer.push_text  s0    yy		&&t'A'A3t9Mr.   c                $   t        | j                        D ]5  }	 |j                         r	 |j                          |j                  |       7 y # t        j
                  $ r Y )w xY w# t        $ r | j                  j                  |       Y xw xY wr   )	listr   full
get_nowaitr	  
QueueEmpty
put_nowaitrG   r  )r   r   r   s      r,   r:  z#WsFrameServer._push_message_in_loop  s    $,,'E,::<((*   ) (
 #--   ,$$U+,s3   A(AA(A%"A($A%%A(($BB)rf   i="  r6   NTN)r;  bytesrW   rZ   )r   r   r   r   r   r  r$  r/  r   r2  r<  r?  r:  r~   r.   r,   r   r   -  s8    
/)NV
*#*	#P
N

,r.   r   c                       e Zd ZddZd Zd Zy)StaticViewerServerNc                   t        t        |      j                               | _        || _        t        |      | _        t        |      | _        |xs t        j                  t              | _        || _        || _        d | _        d | _        y r   )rZ   r   r)   	directoryrr   ri   r   rv   r   r   r   rP   r   handler_factoryhttpdr   )r   rK  rr   rv   rP   r   rL  s          r,   r   zStaticViewerServer.__init__  sm    T)_4467	!$iI	; 1 1( ;".
r.   c                J   | j                   ry | j                  r%t        | j                  | j                        | _        | j                  r| j                  | j                        }nt        t        | j                        }t        | j                  | j
                  f|      | _         t        j                  | j                   j                  d      | _        | j                  j                          | j                   j#                  d| j                  | j
                         y )N)rK  Tr   z$HTTP viewer http://%s:%s/viewer.html)rM  r   rx   rr   r   rv   rL  rK  r   r   r   r   r   serve_foreverr   r   rP   rM   )r   r  s     r,   r   zStaticViewerServer.start  s    ::>>&tyy$2E2EFDI**4>>:G6$..QG($))TYY)?I
&&djj.F.FtT?DIIVr.   c                    | j                   r<| j                   j                          | j                   j                          d | _         y y r   )rM  shutdownserver_closer   s    r,   r   zStaticViewerServer.stop  s5    ::JJ!JJ##%DJ r.   )rf   i@  NTN)r   r   r   r   r   r   r~   r.   r,   rI  rI    s    	Wr.   rI  c                  .    e Zd ZddZddZd Zd Zd Zy)	CaptureWorkerc                $   || _         || _        || _        t        |      | _        t        |      | _        t        |      | _        t        |      | _        d| _	        d | _
        d| _        d| _        ddd| _        i | _        d| _        d| _        y )NFr   r6   r   )frame_serversource_managerrP   ri   fpsjpeg_quality	max_widthr   draw_borderr   r   frame_countlast_target_titlelast_frame_sizelast_capture_rectlast_capture_modelast_capture_saved_path)r   rV  rW  rP   rX  rY  rZ  r[  s           r,   r   zCaptureWorker.__init__  s    (,s8-Y,!#)*a8!#!#')$r.   Nc           	         |t        dt        |            | _        |$t        dt        dt        |                  | _        |t        dt        |            | _        |t        |      | _        y y )Nr5   _   r   )maxri   rX  minrY  rZ  r   r[  )r   rX  rY  rZ  r[  s        r,   update_optionszCaptureWorker.update_options  sj    ?1c#h'DH# #As2s</@'A BD  C	N3DN"#K0D #r.   c                p   | j                   r| j                  j                  d       y d| _         t        j                  | j
                  d      | _        | j                  j                          | j                  j                  d| j                  | j                  | j                  | j                         y )Nzcapture already runningTr   z9capture started: fps=%s quality=%s max_width=%s border=%s)r   rP   rM   r   r   r/  r   r   rX  rY  rZ  r[  r   s    r,   r   zCaptureWorker.start  s    <<KK67&&diiEGHHNN	
r.   c                    d| _         y r}   )r   r   s    r,   r   zCaptureWorker.stop  s	    r.   c           	        t        j                          5 }| j                  rt        j                         }	 | j                  j                         }|st        j                  d       T|j                  }t        |      | _	        t        |j                        | _        |j                  |      }t        j                  d|j                   |j"                  dd      }| j$                  rWt'        j(                  |      }|j                   \  }}	t+        d      D ]%  }
|j-                  |
|
|dz
  |
z
  |	dz
  |
z
  fd       ' | j.                  r|j0                  | j.                  kD  rot3        |j4                  | j.                  |j0                  z  z        }|j7                  | j.                  t9        d|      ft        j:                  j<                        }t3        |j0                        t3        |j4                        d	| _        tA        jB                         }|jE                  |d
| jF                  d       | jH                  jK                  |jM                                | xjN                  dz  c_'        |jP                  | _)        dt9        dt3        | jZ                              z  }t        j                         |z
  }t        j                  t9        d||z
               | j                  rd d d        y # tT        $ r;}| jV                  jY                  d|       t        j                  d       Y d }~d }~ww xY w# 1 sw Y   y xY w)Nr   RGBrS   BGRXr   r5   )   r   r   )r   r   JPEGFformatqualityoptimizezcapture error: %s      ?g      ?gMbP?).mssr   r   perf_counterrW  
get_targetr   monitor_dictdictr_  rZ   moder`  grabr   	frombytessizebgrar[  r   Drawrh   	rectanglerZ  r   ri   r   resizerd  
ResamplingBILINEARr^  ioBytesIOsaverY  rV  r<  getvaluer\  r   r]  rG   rP   r   rX  )r   sctstartedr   monitorshotpildrawwhi
new_heightbufrV   intervalelapseds                   r,   r/  zCaptureWorker._run  sp   WWY#,,++-$!00;;=F!

3 $11G-1']D*-0-=D*88G,D//%DIIufUC''(~~c2"xx1!&qA NNAq!a%!)QUQY+GQ\N] "* ~~#))dnn*D%(t~~		7Q)R%S
!jj$..#a:L)MuO_O_OhOhi58^sSVS]S]+_D(**,CHHS9J9JUZH[%%00@$$)$-3\\D*
 QDHH!66++-7

3uh&89:I ,, Y> ! $KK%%&93?JJsOO$? YsB   "M 1K9)M *HK9A'M 9	L=1L83M 8L==M  M	)   F      T)NNNN)r   r   r   r   rf  r   r   r/  r~   r.   r,   rT  rT    s    *"1
&;r.   rT  c                     e Zd Zd Zed@d       ZdAdZdBdZedCd       ZdDdZ	dEdZ
dBdZdBd	ZdFdGd
ZdBdZdHdBdZdBdZdIdJdZdKdZdHdLdZdKdZdMdNdZdBdZdBdZedOd       ZdPdZdQdZdQdZdQdZdRdZdSdZdTdZdTdZ edUd       Z!d  Z"d! Z#d" Z$dVd#Z%d$ Z&d% Z'd& Z(dHd'Z)d( Z*d) Z+d* Z,d+ Z-d, Z.d- Z/d. Z0d/ Z1d0 Z2d1 Z3d2 Z4d3 Z5dWd4Z6d5 Z7d6 Z8d7 Z9dXd8Z:dSd9Z;d: Z<d; Z=d< Z>d= Z?d> Z@d? ZAy)Y
Win2RtcAppc                    t                | _        | _        t        j                  d       _         j
                  j                  |j                  rt        j                  nt        j                         t        j                         }|j                  t        j                  d              j
                  j                  |        j                  j                  d       t        t               j#                         j$                  }| _        t(        t)        ddt*              nd  _        d _         j1                  |       _        |dz   _         j7                          _        d  _         j=                          t?        tA        |      tC        |jD                         j
                  	       _#        tI         jF                         _%        |jD                  r jJ                  r jJ                  d
   nd _"        tM         j
                         _'        tM        jP                          _)        |dz   _*        tW        |dd      r#t        |jX                        j#                         nd  _,        g  _-         j8                  j\                  }t_         j`                         _1        te        jf                  tC        |jh                               _5        te        jf                  tC        |jl                               _7        te        jf                  tC        |jp                               _9        tu         j                  d       _;        |dz   _<        t{         jb                   fd j
                   jv                  j|                         _4        t         j                   fd j                   jv                  j|                   j
                  dd       _A         j                  j                          t        |j                  |j                   jD                   j
                  |j                    j                         _H         fd}t         jT                  |j                  |j                   j
                  |j                   |       _K        t         j                   jb                   j
                  t        dt        |j                  xs |j                              t        dt        dt        |j                  xs |j                                    t        d
t        |j                  |j                  n|j                              tC        |j                               _U        t         jb                   j
                   fd        _W         j8                  j\                  }te        j                  d!       _Y        te        j                  t        |j                  xs d"             _\        te        j                  t        d
t        |j                  xs d
                   __        te        jf                  tC        |j                               _a        te        jf                  tC        |j                               _b        te        j                  t        dt        |j                  xs |j                                     _c        te        j                  t        dt        dt        |j                  xs |j                                           _d        te        j                  t        d
t        |j                  |j                  n|j                                     _e        te        jf                  tC        |jh                               _5        te        jf                  tC        |jl                               _7        te        jf                  tC        |jp                               _9        te        j                  d#       _f        te        j                  d$       _g        te        j                  d%       _h         j8                  j                  }te        jf                  tC        |j                               _k        te        j                  t        |j                               _m        te        j                  t        |j                               _o        te        j                  t        |j                               _s        te        j                  t        |j                               _u        te        j                  t        |j                               _w        te        j                  t        |j                               _x        te        j                  t        |j                               _y        t         j                   j
                   j                   fd& fd'(       _|         j                  j                           j                           jb                  j                           j                  j                   j8                  j                          j                  j                  d) j                          j                  j                  d* j                  d+,        j                           j                           j                  d-        j                  j                  d. j                          j                  j                  d/ j                          j                  j                  d0 j                          j                  j                  d1 j                         y )2Nr   z%(levelname)s: %(message)sz,RTC8790_8791 - Windows Remote Desktop BridgeRTC8790_8791u)   RTC8790_8791 - 클릭하여 화면 복원)	icon_pathFzrtc8790_8791_config.json)rO   rP   r   r6   web
state_file)valuei  )hold_ms
recv_filesc                 8     j                   j                         S r   )control_varr[   r   s   r,   r   z%Win2RtcApp.__init__.<locals>.<lambda>b  s    (8(8(<(<(>r.   )is_enabled_callbackrP   status_callbackc                 8     j                   j                         S r   )clipboard_varr[   r   s   r,   r   z%Win2RtcApp.__init__.<locals>.<lambda>h  s    (:(:(>(>(@r.   i  i@ )r  notify_viewers_callbackr  rP   poll_ms	max_chars)r   r   c           	         t        | j                  j                  fdj                  j                  j
                  j                        }t        j                         G fdd|      }|S )Nc                 8     j                   j                         S r   )file_transfer_varr[   r   s   r,   r   zDWin2RtcApp.__init__.<locals>._http_handler_factory.<locals>.<lambda>~  s    D,B,B,F,F,Hr.   )web_dirrecv_dirr8   r  r  web_notify_callbackrP   c                  f     e Zd ZdZdZdddddZdd	d
dddZdddZdfdZ fdZ	 fdZ
 xZS )JWin2RtcApp.__init__.<locals>._http_handler_factory.<locals>.RtcHttpHandleru-  RTC viewer에서 Auto8700 CDP API를 안전하게 호출하기 위한 host-side 프록시.

                원격 모바일 브라우저의 127.0.0.1은 host PC가 아니므로, viewer JS가
                Auto8700 8707을 직접 호출하지 않고 RTC HTTP 서버를 경유한다.
                zhttp://127.0.0.1:8707z/api/cdp/statusz/api/cdp/targetsz/api/cdp/ig/statusz/api/cdp/ig/schedule_storage)z/api/auto8700/cdp/statusz/api/auto8700/cdp/targetsz/api/auto8700/cdp/ig/statusz%/api/auto8700/cdp/ig/schedule_storagez/api/cdp/evaluatez/api/cdp/bring_to_frontz/api/cdp/ig/bring_to_frontz/api/cdp/ig/send_eventz/api/cdp/ig/side_open)z/api/auto8700/cdp/evaluatez /api/auto8700/cdp/bring_to_frontz#/api/auto8700/cdp/ig/bring_to_frontz/api/auto8700/cdp/ig/send_eventz/api/auto8700/cdp/ig/side_openc           	     n   	 | j                   |z   }ddi}d }|j                         dk(  r|xs d}d|d<   t        j                  j	                  |||j                         |      }t        j                  j                  |d	      5 }|j                         }	t        t        |d
d      xs d      }
|j                  j                  d      xs d}d d d        | j                  
       | j                  d       | j                  dt        t        	                   | j                  dd       | j                          | j                   j#                  |	       y # 1 sw Y   xY w# t$        $ r}t'        j(                  ddt        |      dd      j+                  d      }| j                  d       | j                  dd       | j                  dt        t        |                   | j                  dd       | j                          | j                   j#                  |       Y d }~y d }~ww xY w)Nz
User-Agentzrtc8790-auto8700-cdp-proxyPOST   {}zapplication/json; charset=utf-8r  )datamethodheadersg      @timeoutstatus   r  zCache-Controlzno-storeFauto8700_proxy_failedr   r   r   ensure_asciir0   i  )auto8700_baseupperurllibr!  Requesturlopenreadri   r  r  r[   send_responsesend_headerrZ   rE   end_headerswfilewriterG   r  r  encode)r   r  target_pathr#  urlr  r  reqresprS   codectyperV   payloads                 r,   _proxy_jsonzVWin2RtcApp.__init__.<locals>._http_handler_factory.<locals>.RtcHttpHandler._proxy_json  s   2"00;>#/1M"N#!<<>V3#'=5D6WGN3$nn44StFLLNdk4l#^^33C3E"&))+C#&wtXs'C'Js#KD$(LL$4$4^$D$iHiE F **40((?(()93s3x=I((*E((*

((- FE % 2"&**ED[hklohp-q  AF  #G  #N  #N  OV  #W**3/((9Z[(()93s7|;LM((*E((*

((112s3   BE* A
EBE* E'#E* *	H43B7H//H4c                R   	 syt        | j                        }t        |j                        j	                  ddg      d   }| j
                  j	                  d      xs | j
                  j	                  d      xs d}t        |      xs t        |      S # t        $ r Y yw xY w)NTr8   r6   r   zX-RTC-TokenzX-Rtc-TokenF)r	   rN   r   r  r[   r  rd   rG   )r   r"  got_qsgot_hdrcdp_proxy_tokenss       r,   _check_cdp_proxy_tokenzaWin2RtcApp.__init__.<locals>._http_handler_factory.<locals>.RtcHttpHandler._check_cdp_proxy_token  s    %/#'!)$))!4!)&,,!7!;!;GbT!J1!M"&,,"2"2="A"jT\\EUEUVcEd"jhj/8HIxM]^egwMxx$ %$%s   B BB 	B&%B&c                   t        | j                        }|j                  | j                  v rK| j                         s| j	                  dd      S | j                  d| j                  |j                           S t        |          S )Nr  invalid tokenGET)r	   rN   cdp_get_mapr  _rejectr  superdo_GET)r   r"  	__class__s     r,   r  zQWin2RtcApp.__init__.<locals>._http_handler_factory.<locals>.RtcHttpHandler.do_GET  so    %dii0F{{d&6&66#::<#'<<_#EE#//t7G7G7TUU 7>++r.   c                   t        | j                        }|j                  | j                  v r| j                         s| j	                  dd      S 	 t        | j                  j                  d      xs d      }|r| j                  j                  |      nd}| j                  d| j                  |j                     |      S t        | 5         S # t        $ r d}Y dw xY w)Nr  r  r  r   r  r  )r	   rN   cdp_post_mapr  r  ri   r  r[   rG   rfiler  r  r  do_POST)r   r"  lengthr#  r  s       r,   r  zRWin2RtcApp.__init__.<locals>._http_handler_factory.<locals>.RtcHttpHandler.do_POST  s    %dii0F{{d&7&77#::<#'<<_#EE'%()9)9:J)K)Pq%QF ;Atzzv6e#//8I8I&++8VX\]] 7?,,	  ) '%&F's   (C CC)r.   )r  rZ   r  rZ   r#  rF  r   Noner   )r   r   r   r   r  r  r  r  r  r  r  __classcell__)r  r  s   @r,   RtcHttpHandlerr    sT    
 !80A1C3G=[	 3F8Q;W7O6M 28	%,- -r.   r  )	r   r  r8   remote_activity_overlayshownotify_viewersrP   rA  
_st_tokens)rK  base_handlerr  r  r   s      @r,   _http_handler_factoryz2Win2RtcApp.__init__.<locals>._http_handler_factoryy  sh    5!jj$H $ < < A A$($7$7{{L  $DOO4O- O-b "!r.   )r   rL  r5   r&  rc  rX  rY  rZ  r[  c                b     t        d      r j                  j                  d fd      S d S )N
status_varr   c                 :    j                   j                         S r   )r  r   msgr   s   r,   r   z7Win2RtcApp.__init__.<locals>.<lambda>.<locals>.<lambda>  s    4??CVCVWZC[r.   )hasattrr   afterr  s   `r,   r   z%Win2RtcApp.__init__.<locals>.<lambda>  s4    `ghlnz`{		;[(\  )F  BF  )Fr.   )r     선택된 창: 없음desktopu
   대기 중u   서버 시작 전u   UPnP 실행 전c                 8     j                   j                         S r   )lock_varr[   r   s   r,   r   z%Win2RtcApp.__init__.<locals>.<lambda>  s    t}}'8'8':r.   c                 >     j                   j                         dk(  S )Nwindow)
source_varr[   r   s   r,   r   z%Win2RtcApp.__init__.<locals>.<lambda>  s    DOO,?,?,AX,Mr.   )r   r   WM_DELETE_WINDOWz<Configure>r   )r  initP   r  i,    )r   r   argsr   r   rP   setLevelverboseDEBUGINFOStreamHandlersetFormatter	Formatter
addHandlerr   r   __file__r)   r*   r+   r   TRAY_ICON_PATHtraytray_hiddenresolve_same_name_config_pathconfig_pathlegacy_config_path&load_app_settings_with_legacy_fallbackapp_settings_settings_save_after_idrestore_main_window_geometryrX   r-   r   r8   _st_token_entriesr_   r  r   upnp
get_lan_iplan_ip
viewer_dirr  r  state_log_linesuir   get_selected_hwndrW  r   
BooleanVarremote_controlr  file_transferr  	clipboardr  r   r  r  r   r  r   r  clipboard_syncr   r   rr   ws_portno_auto_porthandle_ws_text_message	ws_serverrI  	http_porthttp_serverrT  rd  ri   rX  re  rY  rp  rZ  r[  capture_workerr!   snapshot_ring	StringVarhwnd_varrZ   sourcer  IntVardisplay_indexdisplay_varlock_windowr  
border_varfps_varquality_varmax_width_varr  local_url_varshare_url_varsnapshotenabledai_snapshot_varfolderai_snapshot_folder_var
keep_countai_snapshot_keep_var	DoubleVarfloatinterval_secai_snapshot_interval_varmin_save_interval_secai_snapshot_min_interval_varchange_threshold_percentai_snapshot_change_varai_snapshot_max_width_varai_snapshot_quality_varrz   on_hwnd_changer   r   setup_uiset_desktopupdate_settingsprotocolon_closerp   on_root_configureinstall_settings_tracesrefresh_local_url_labelwrite_stater  hide_to_tray_on_startuppoll_tray_restoreapply_startup_optionsperiodic_state_write)r   r   r  r  r+   r  r  snaps   `       r,   r   zWin2RtcApp.__init__9  s   		''6dllW]]M'')W../KLMw'		FG>))+22  N  ZON4_kyz  `d	 ==hG"*-G"G GGI'+$))+!67I(7S\`aeakak\luy  vA  vA  "B)$*@*@A-1ZZDOOdooa(RT
"4;;/	$//1"U*=DT<Y[=\$t/779bf+-!!243I3IJ==tB4E4E/FG!#T":J:J5K!L]]bll1CD'<TYYPT'U$ </3 >;; 88==	
 3II @$($7$7 88==;;
 	!!#&IILLJJKK+++ 77
^	"@ .OOIINNKK+++1
 ,NNKKAs266-TXX./RRR__-L)M!NO!S1It~~^_R^^,
 0KK F
 !!+BC,,S1Gi-HI993q#b6F6F6K!2L+MND,@A--d2>>.BCyys1c"&&2DDHH.E'FG993r3r3r?^RVR^R^;_3`+abYYSCH`fjftft4u-vw==tB4E4E/FG!#T":J:J5K!L]]bll1CD,,\:\\0CD\\0AB  ))!}}43EF&(llT[[9I&J#$&IIC4H$I!(*5ARAR;S(T%,.LLuTE_E_?`,a)&(llt?\?\9]&^#)+T^^9L)M&')yys4;L;L7M'N$$IIKK:$M
 	%%''')**4+<+<+E+EF		-t}}=		}d&<&<#F$$&$$& 		D889		T334		T778		d778r.   c                   	 t        t        j                  d         j                         }|j	                         r-|j
                  j                         dv r|j                  d      S | dz  S # t        $ r Y | dz  S w xY w)uQ   실행한 PY 파일과 동일한 파일명.json을 설정 파일로 사용한다.r   )z.pyz.pywz.exez.jsonzrtc8790_8791_main.json)	r   sysargvr)   r?   suffixlowerwith_suffixrG   )r+   argv0s     r,   r  z(Win2RtcApp.resolve_same_name_config_path!  s    	!%--/E||~%,,"4"4"6:Q"Q((11 222  	222	s   A&A. .	A>=A>c                   	 | j                   j                         rt        | j                         S | j                  j                         rt        | j                        S 	 t               S # t        $ r Y t               S w xY wr   )r  r?   r   r  rG   r   r   s    r,   r  z1Win2RtcApp.load_app_settings_with_legacy_fallback,  sx    	&&($T%5%566&&--/$T%<%<== 0 }  	}	s   .A+ .A+ +	B ?B c                    t         j                  dt                     }t        t        |dd      xs d      j	                         } j                  |      sd}	  j                  j                  |       t        t        |dd      xs d      j                         }|dk(  r  j                  j                  d fd	       y y # t        $ r  j                  j                  d       Y tw xY w)
Nr  window_geometryr6   940x850window_statenormalzoomedd   c                 &     j                  d      S )Nrd  )safe_set_root_stater   s   r,   r   z9Win2RtcApp.restore_main_window_geometry.<locals>.<lambda>A  s    )A)A()Kr.   )r  r  r   rZ   rB   is_safe_tk_geometryr   r   rG   r[  r  )r   r  r   states   `   r,   r  z'Win2RtcApp.restore_main_window_geometry6  s    T&&jl;wr#4b9?R@FFH''1 H	*IIx( GB9EXFLLNHIIOOC!KL   	*IIy)	*s   C $C-,C-c           	     Z    dd l }t        |j                  dt        | xs d                  S )Nr   z(\d{3,5}x\d{3,5}([+-]\d{1,6}[+-]\d{1,6})?r6   )rer   	fullmatchrZ   )r  rk  s     r,   rh  zWin2RtcApp.is_safe_tk_geometryC  s(    BLL!LcRWR][]N^_``r.   c                Z    	 | j                   j                  |       y # t        $ r Y y w xY wr   )r   ri  rG   )r   ri  s     r,   rg  zWin2RtcApp.safe_set_root_stateH  s)    	IIOOE" 		s    	**c                R   	 t        | j                  j                         xs d      }|dk(  rd}	 | j                  j	                         }| j                  |      sd}t        ||t        | j                  j                         xs d      t        dt        | j                  j                         xs d            t        | j                  j                               t        | j                  j                               t        | j                  j                               t        | j                   j                               t        dt        | j"                  j                         xs d            t        dt%        d	t        | j&                  j                         xs d
                  t        dt        | j(                  j                         xs d            t        | j*                  j                                     S # t        $ r d}Y w xY w# t        $ r d}Y w xY w)Nrc  iconicra  r  r   r5   r  r&  rc  r  )r`  rb  r,  r.  r0  r  r  r   rX  rY  rZ  r[  )rZ   r   ri  rG   r   rh  r   r  r[   rd  ri   r/  r   r  r  r  r  r2  re  r3  r4  r1  )r   ri  r   s      r,   build_ui_settingszWin2RtcApp.build_ui_settingsN  s   			)5X6E HE	!yy))+H ''1 H$t**,9	:aT%5%5%9%9%;%@q!ABT]]..01 0 0 4 4 67t5599;<4--1134As4<<++-345RRT-=-=-A-A-C-Ir)J!KL!S!3!3!7!7!9!>Q?@T__0023
 	
  	E	  	! H	!s"   'H H HHH&%H&c                    | j                         | j                  _        | j                         | j                  _        t        | j                  | j                         y r   )rp  r  r  build_ai_snapshot_settingsr7  r    r  r   s    r,   save_all_settingszWin2RtcApp.save_all_settingsj  sF    #557%)%D%D%F"d&&(9(9:r.   c                    	 | j                          y # t        $ r<}	 | j                  j                  d|       n# t        $ r Y nw xY wY d }~y Y d }~y d }~ww xY w)Nzsettings save failed: %s)rs  rG   rP   r   r7  s     r,   save_all_settings_quietlyz$Win2RtcApp.save_all_settings_quietlyo  sX    	""$ 	!!"<cB  C	s/    	A:A	AAAAAc                .   	 | j                   %| j                  j                  | j                          	 | j                  j	                  t        |      | j                        | _         y # t        $ r Y Aw xY w# t        $ r | j                          Y y w xY wr   )r  r   after_cancelrG   r  ri   _finish_scheduled_settings_saveru  )r   delay_mss     r,   schedule_settings_savez!Win2RtcApp.schedule_settings_savex  s    	++7		&&t'C'CD	-+/99??3x=$JnJn+oD(  		  	-**,	-s"   1A) 4A8 )	A54A58BBc                2    d | _         | j                          y r   )r  ru  r   s    r,   rx  z*Win2RtcApp._finish_scheduled_settings_save  s    '+$&&(r.   Nc                |    	 ||j                   | j                  ury | j                  d       y # t        $ r Y y w xY w)Nr   )widgetr   rz  rG   )r   events     r,   rN  zWin2RtcApp.on_root_configure  s?    	 U\\%B''- 		s   / / 	;;c                v     d}|D ]"  }	 t         |      j                  d fd       $ y # t        $ r Y 1w xY w)N)r  r/  r  r1  r2  r3  r4  r  r  r  r9  r;  r=  rA  rC  rE  rF  rG  r  c                 $    j                         S r   )rz  )_r   s    r,   r   z4Win2RtcApp.install_settings_traces.<locals>.<lambda>  s    $B]B]B_r.   )r  	trace_addrG   )r   namesnames   `  r,   rO  z"Win2RtcApp.install_settings_traces  sG    
 Dd#--g7_`   s   ,	88c                    	 t        j                  d      t        |      t        |      t        |      d}| j                  j	                  |       | j                  dd  | _        y # t
        $ r Y y w xY w)Nz%H:%M:%S)tssrcr   leveli8)r   strftimerZ   r  rF   rG   )r   r  r   r  items        r,   append_state_logzWin2RtcApp.append_state_log  sk    	--
3CHQTU\Q]hklqhrsD  ''-#'#7#7#>D  		s   A$A' '	A32A3c                ~   	 | j                   j                         }i }t        t	        | j                   dd      xs d      }d}|r|j
                  \  }}}}t        |      t        |      t        |      t        |      t        ||z
        t        ||z
        d}t        |j                  xs |      }t        |j                  xs d      }nI	 t        t	        | j                  di       xs i       }t        t	        | j                  d|      xs |      }d}	d}
	 | j                         }	| j                         }
t        t        t        j                          t        | j"                        |	|
t%        t	        | j&                  dg       xs g       |||xs t	        | j                  dd      t	        | j                  dd	d	d
      t        t	        | j                  dd	      xs d	      t)        t	        | j                  dd            ddt)        | j*                  j-                               i| j.                  j0                  t        t	        | j2                  d| j.                  j4                              d| j.                  j0                  t        t	        | j&                  d| j.                  j6                              dd	di d| j9                  |      t        t	        | j                  dd      xs d      | j:                  dd  dS # t        $ r d }Y w xY w# t        $ r Y w xY w# t        $ r Y w xY w)Nrx  r6   )lefttoprightbottomr   r   r_  r`  r   r]  r^  r   r   r\  r   F)rx  r   r   
frame_sizer\  r   r8  rv   )rr   rv   main_py_rtc_no_marker_scan)foundreasonmarkersra  i)	build_tagbuild_versionpidr  	local_urllan_urlr   capturer  httpwsmarkercursorra  	log_lines)rW  ru  rG   rZ   r  r   ri   rx  r   rw  r(  get_local_viewer_urlget_lan_viewer_url	BUILD_TAGBUILD_VERSIONosgetpidr  rE   r%  r   r  r[   r  rr   r'  r&  r"  get_cursor_stater  )r   r   r   rx  r   r  r  r  r  r  r  s              r,   build_statezWin2RtcApp.build_state  s   	((335F 74..;ArB'-{{$D#ufIc#hUWZ[aWbmpqvw{q{m|  IL  MS  TW  MW  IX  YDv{{*d+D*+EGD$7$79LbQWUWX74#6#68KTRZVZ[ 		113I--/G #*99;t//0"74>>9bAGRHW'$*=*=?RTV"W%d&9&9;LXYefNgh"74+>+>q#Q#VUVW(;(;Y NO "4(8(8(<(<(>#?@!YY^^SAQAQSY[_[d[d[n[n9o5pq99>>3wt~~vW[W`W`WhWh7i3jk !-IVXY++D1'*743F3FHace+f+ljl'm--de4-
 	
/  	F	    		s6   L AL  L/ LL	L,+L,/	L<;L<c                   	 t        j                         \  }}t        |t              r|n#t        t	        | j
                  di       xs i       }t        |j                  dd      xs d      }t        |j                  dd      xs d      }t        |j                  dd      xs d      }t        |j                  dd      xs d      }t	        | j
                  di       xs i }t        |j                  dd      xs d      }	t        |j                  dd      xs d      }
d }d }d}|dkD  r|dkD  r|	dkD  r{|
dkD  rv|t        |      cxk  xr ||z   k  nc xr |t        |      cxk  xr ||z   k  nc }t        t        |      |z
  |	z  |z  d	      }t        t        |      |z
  |
z  |z  d	      }d
t        |      t        |      ||t        |      ||||d|	|
ddS # t        $ r}dt        |      dcY d }~S d }~ww xY w)Nr_  r  r   r  r   r   r^  Fr5   T)r  r  r   r   r   )r   r   r   frame_xframe_yinside_capturecapture_rectr  )r   r   )win32apir   r   rw  r  r(  ri   r[   roundr   rG   rZ   )r   r   r   r   r  r  r   r   r   fwfhr  r  r  rV   s                  r,   r  zWin2RtcApp.get_cursor_state  s   	4((*DAq%dD14tGDDWDWYlnp<q<wuw7xDtxx*/a0Ddhhua(-A.C!,12E(A.3!4FD//1BBGM2EUYYw*/a0BUYYx+0q1BGG"NqyVaZBFrAv!%Q!>$,!>!_3#a&C_SVY_S_C_Q$" 4u <a@Q# 3f <a@VV"""&~"6)-cEU[ \(*b9	 	  	4#c(33	4s   GG" "	H+G>8H>Hc                l   	 | j                   j                         }|sddddS | j                  dz  }|j                  dd       t	        j
                  d      }|d	| d
z  }t        j                         5 }|j                  |j                        }t        j                  d|j                  |j                  dd      }|j                  |dt        | j                  j                         d       d d d        t#        |      | j                  _        | j'                  dd|        | j)                  d       dt#        |      d| |j*                  t-        |j                        dS # 1 sw Y   zxY w# t.        $ r2}| j'                  dd| d       dt#        |      d| dcY d }~S d }~ww xY w)NFcapture_target_missingu   캡처 대상이 없습니다r  capturesTr:   z%Y%m%d_%H%M%Srtc_capture_z.jpgrj  rS   rk  rm  rn  RTCu   캡처 저장 capture_savedu   캡처 저장: )r   rN   r   rx  r   u   캡처 저장 실패: r   )rW  ru  r+   rI   r   r  rs  ry  rv  r   rz  r{  r|  r  ri   r(  rY  rZ   ra  r  rQ  rx  rw  rG   )	r   r   out_dirstamprN   r  r  r  rV   s	            r,   save_current_capturezWin2RtcApp.save_current_capture  s   	_((335F#.FSrssmmj0GMM$M6MM/2E|E7$77Dcxx 3 34ooeTYY		5&Qfc$:M:M:Z:Z6[fkl  ;>d)D7!!%>$)@A_-D	PTv>V`f`k`kuy  {A  {N  {N  vO  P  P   	_!!%+A#)GQ#c(AWX[W\?]^^	_s=   !E8 AE8 8A;E,3A8E8 ,E51E8 8	F3'F.(F3.F3c                   t        | dd       sy 	 | j                         }|rt        |      |d<   | j                  j                  j                  dd       | j                  j                  t        j                  |dd      d	       y # t        $ r<}	 | j                  j                  d
|       n# t        $ r Y nw xY wY d }~y Y d }~y d }~ww xY w)Nr  state_reasonTr:   Fr   )r  indentr0   r>   zstate write failed: %s)r  r  rZ   r  r*   rI   rL   r  r  rG   rP   r   )r   r  r  rV   s       r,   rQ  zWin2RtcApp.write_state  s    t\40
	##%D'*6{^$OO""(((EOO&&tzz$UST'U`g&h 	!!":C@  A	s6   A:B 	CB21C2	B>;C=B>>CCc                ,   	 | j                  d| j                         d       	 | j                  j                  d| j                         y # t
        $ r Y y w xY w# 	 | j                  j                  d| j                         w # t
        $ r Y w w xY wxY w)N	rtc_state)r  ri  r   )r  r  r   r  rU  rG   r   s    r,   rU  zWin2RtcApp.periodic_state_write  s    	t?O?O?Q RS		d&?&?@ 		d&?&?@ s@   "A &A 	AAB&BB	BBBBc                   	 t        t        | j                  dd      xs d      j                         }|dk(  rp| j                  j                  t        dt        t        | j                  d| j                  j                               xs d                   | j                          nr|dk(  r,| j                  j                  d       | j                          nA|dk(  r,| j                  j                  d       | j                          n| j                          t        t        | j                  dd      xs d      j                         }|d	v r,| j                  j                  d
       | j                          n/|dv r+| j                  j                  d       | j                          t        t        | j                  dd            r| j                          | j!                  dd       | j#                  d       t        t        | j                  dd      xs d      j                         dk(  rGt%        j&                  | j)                                | j!                  dd       | j#                  d       yyy# t*        $ re}	 | j,                  j                  d|        | j!                  dd| d       | j#                  d       n# t*        $ r Y nw xY wY d}~yY d}~yd}~ww xY w)uO   WS에서 실행했을 때 원격 데몬과 viewer를 자동으로 연결한다.r,  r6   displayr   r.  r  r  r  )1trueyesonu   켜기T)0falsenooffu   끄기F
auto_startr  zhost/main.py auto-start previewviewerr  openzviewer opened from host/main.pyviewer_openu   WS 자동 RTC 시작 실패: zauto-start failed: r   auto_start_failedN)rZ   r  r  r[  r/  r   rd  ri   r[   on_display_selectedr  on_source_changedr  on_control_changedr   start_previewr  rQ  
webbrowserr  r  rG   r  )r   r,  control_argrV   s       r,   rT  z Win2RtcApp.apply_startup_options'  sm   "	Hb9?R@FFHF"  $$SC		?TXTdTdThThTj0k0pop,q%rs((*8###H-&&(9$##I.&&(&&(gdiiB?E2FLLNKBB  $$T*'') EE  $$U+'')GDII|U;<""$%%e-NO  .wtyy(D9ATBHHJfTOOD$=$=$?@))%1RS$$]3 U	 =  	##&CC5$IJ%%e/B3%-H'R  !45  6		s7   I8I= =	K+AKK&	KK&KK&&K+c                 F    t         5  t        cd d d        S # 1 sw Y   y xY wr   )r   r&   r~   r.   r,   r  zWin2RtcApp.get_selected_hwndM  s      YYs    c                2    | j                   j                  dv S )N)rf   r6   N)r  rr   r   s    r,   is_bound_allzWin2RtcApp.is_bound_allR  s    yy~~!666r.   c                R    | j                         rdS | j                  j                  S )Nz	127.0.0.1)r  r  rr   r   s    r,    get_http_url_host_for_local_openz+Win2RtcApp.get_http_url_host_for_local_openU  s     "//1{Etyy~~Er.   c                L   | j                         r| j                  n| j                  j                  }| j                         r| j                  n| j                  j                  }t	        || j
                  j                  || j                  j                  | j                        S r   )	r  r  r  rr   r   r'  rv   r%  r8   )r   	http_hostws_hosts      r,   r  zWin2RtcApp.get_lan_viewer_urlX  sn    #'#4#4#6DKKDIINN	!%!2!2!4$++$))..y$*:*:*?*?$..J]J]_c_i_ijjr.   c                    | j                         }|}t        || j                  j                  || j                  j                  | j
                        S r   )r  r   r'  rv   r%  r8   )r   rr   r  s      r,   r  zWin2RtcApp.get_local_viewer_url]  sE    446tT%5%5%:%:GT^^EXEXZ^ZdZdeer.   c                    	 | j                   j                  t        j                  |d             y # t        $ r&}| j
                  j                  d|       Y d }~y d }~ww xY w)NFr  zviewer notify failed: %s)r%  r?  r  r  rG   rP   r   )r   r  rV   s      r,   r  zWin2RtcApp.notify_viewersb  sL    	?NN$$TZZe%LM 	?KK8#>>	?s   03 	A"AA"c                   	 t        j                  |      }t        |t              r)|j                  d      t        k(  r| j                  |      S t        |t              r%|j                  d      dk(  r| j                  |      S | j                  j                  |      }|rt        |dd      dk7  r|S | j                  j                  |      S # t        $ r d}Y w xY w)uN   viewer.html에서 들어오는 JSON 메시지를 기능별로 라우팅한다.Nr  rtc_chatr   r6   r   )r  loadsrG   r   rw  r[   r   handle_host_option_messagehandle_rtc_chat_messager!  handle_text_messager  r  )r   r   r  r  s       r,   r$  z!Win2RtcApp.handle_ws_text_messageh  s    	::g&D dD!dhhv&6:M&M22488dD!dhhv&6*&D//55$$88Agfi4	AM""66w??  	D	s   C CCc           
        ddl m} t        |j                  d      xs |j                  d      xs d      j	                         }	 |dv r!d}| j                  d|d	|d
        |d	|      S |dk(  r| j                         }t        |t              r|j                  di       ni }t        |t              r|j                  di       ni }d|j                  d       d|j                  d       d|j                  d       d| }| j                  d|d	||d        |d	|      S |dk(  r| j                         }|j                  d      rKd|j                  d       d|j                  d       d|j                  d       d|j                  d       d	nd |j                  d!       }| j                  d|t        |j                  d            ||d"        |t        |j                  d            |      S |d#k(  r| j                         }t        |t              r|j                  di       ni }d$|j                  d       d%|j                  d&       d|j                  d       }| j                  d|d	||d'        |d	|      S |d(k(  r| j                         }| j                  d|t        |j                  d            |j                  d)      xs d|d'        |t        |j                  d            t        |j                  d)      xs |j                  d!      xs d            S d*| }| j                  d|d+|d
        |d+|      S # t        $ r.}	d,|	 }| j                  d|d+|d
        |d+|      cY d }	~	S d }	~	ww xY w)-Nr   ControlResultrW   cmdr6   )?z/helphelpui   명령: /status 현재상태, /capture 캡처정보, /cursor 포인터 위치, /save_capture 수동저장rtc_chat_resultT)r  r  r   r   z/statusr  r  u   상태: capture=rx  z	 running=r   z frame=r  z cursor=)r  r  r   r   ri  z/cursorr   u   포인터: screen=(r   ,r   z	) frame=(r  r  )u   포인터 실패: r   )r  r  r   r   r  z/captureu   캡처정보: mode=z rect=r   )r  r  r   r   r  z/save_capturer   u   알 수 없는 RTC 명령: Fu   RTC 명령 처리 실패: )yjm_remote_controlr  rZ   r[   rB   r  r  r   rw  r  r   r  rG   )
r   r  r  rW   r  stcapcurresrV   s
             r,   r  z"Win2RtcApp.handle_rtc_chat_messagez  s   4488F#<txx<"=CCE!	--- B##->tSWdg$hi$T3//y %%'/9"d/CbffY+.8T.BbffXr*((93779CUBVV]^a^e^efr^s]tt|  ~A  }B  C##->tSWdgrt$uv$T3//y ++- AD  AH  AH  IM  AN+CGGCL>3773<.	RURYRYZcRdQeefgjgngnoxgyfzz{|  Vh  il  ip  ip  qx  iy  hz  T{##->tSWX[X_X_`dXeSfsv  CF  %G  H$T#''$-%8#>>z!%%'/9"d/CbffY++CGGFO+<F3776?BSSZ[^[b[bco[pZqr##->tSWdgtw$xy$T3//&//1##->tSWX[X_X_`dXeSfsvszsz  |E  tF  tL  JL  Y\  %]  ^$T#''$-%8#cggi>P>qTWT[T[\cTd>qhq:rss/v6C):4uad ef ,, 	-.se4C):4uad ef ,,	-s@   $M ,B7M $CM 9BM 
B&M 1#M 	N#NNNc                   ddl m} | j                  j                         s	 |dd      S t	        |j                  dd            }|j                  d      }d}d}|d	k(  rht        |      }| j                  j                  |       | j                  j                  |
       d}d|rdnd }| j                  j                  |       t        t        ||||      }| j                  |        |||      S )Nr   r  Fz,host option blocked: remote control disabledr  r6   r  zunknown optionr[  )r[  Tu   빨간 테두리 ONOFF)r  r  r   r   )r  r  r  r[   rZ   r   r1  r   r(  rf  r  r   r   r  )	r   r  r  r  r  r   r   flagr  s	            r,   r  z%Win2RtcApp.handle_host_option_message  s    4##% (VWW488FB'(!"= ;DOO%..4.@B)$$E)BCGOO(.T2W^_G$R))r.   c                   |j                  d      rdnd}|j                  d      rd|j                  d       nd}|j                  d      rd|j                  d       nd}|j                  d| dz         }d	|  d
| dt        |d          dt        |d          dt        |d          dt        |d          | | | S )Nprimaryu    / 주모니터r6   r,   / deviceindexr5      디스플레이 z / mss[z] - r   r   r   z @ r  r  r  )r[   ri   )idxmr  r,  r  
real_indexs         r,   format_display_labelzWin2RtcApp.format_display_label  s    '(uuY'7#R,-EE(O3quuX'(,-EE(O3quuX'(UU7C!G,
se7:,d3qz?:K1SQRS[Q\M]L^ _QvY #ah-	&&K	
r.   c           	     X   | j                   j                         }t        |      D cg c]  \  }}| j                  ||       }}}|sdg}|| j                  d<   | j
                  j                         }|rt        t        d|      t        |      dz
        }| j
                  j                  |       | j                  j                  |       | j                   j                  |       | j                  j                  dt        |       d       t        |      D ]2  \  }}| j                  j                  d|| j                  ||             4 y | j
                  j                  d       | j                  j                  d       | j                  j                  d       y c c}}w )	Nu   디스플레이 없음valuesr   r5   r  u   개 감지됨zdisplay %s: %su/   디스플레이를 감지하지 못했습니다)rW  refresh_displays	enumerater   display_combor/  r[   re  rd  rE   r   currentset_displayr  rP   rM   )r   displaysr  r  display_namesr  s         r,   refresh_display_comboz Win2RtcApp.refresh_display_combo  sh   &&779IRS[I\]I\vsA223:I\]56M'48$""&&(#a/3x=1+<=G  )&&w/++G4OO"23x=/ OP#H-Q  !138Q8QRUWX8YZ .   #&&q)OO QR# ^s   F&c                   	 t        t              j                         j                  dz  }t	        j
                  t        j                  t        |      gt        |j                               | j                  j                  d       y# t        $ rD}| j                  j                  d|        | j                  j                  d|       Y d}~yd}~ww xY w)u/   별도 모니터 진단 도구를 실행한다.zyjm_monitor_lab.py)cwdu-   모니터 진단 도구를 실행했습니다u    모니터 진단 실행 실패: zmonitor lab open error: %sN)r   r
  r)   r*   
subprocessPopenrX  
executablerZ   r  r   rG   rP   r   )r   scriptrV   s      r,   open_monitor_labzWin2RtcApp.open_monitor_lab  s    	A(^++-447KKFcnnc&k:FMM@RSOO OP 	AOO"B3% HIKK:C@@	As   BB 	C:CCc                   t        j                  | j                  d      }|j                  t        j
                  d       t        j                  |dd      j                  d	       t        j                  |d
      }|j                  t        j                  d       t        j                  |dd| j                  | j                        j                  ddddd       t        j                  |dd      | _        | j                  j                  d| j                          t        j                  |dd| j                  | j                        j                  ddddd       | j                  j                  ddddd       t        j                  |      }|j                  ddddd       t        j"                  |d| j$                        j                  t        j&                  d !       t        j"                  |d"| j(                        j                  t        j&                  #       t        j                  |      }|j                  ddd$ddd%&       t        j                  |d'd(| j                  | j                        j                  t        j&                  #       |j+                  dd)       | j%                          t        j                  || j,                  *      j                  dd +       t        j.                  |d,| j0                  | j2                  -      j                  dd.+       t        j.                  |d/| j4                  | j6                  -      j                  dd.+       t        j                  |      }|j                  t        j                  d.       t        j.                  |d0| j8                  | j:                  -      j                  t        j&                  #       t        j"                  |d1| j<                        j                  t        j&                  d2!       t        j                  |      }|j                  t        j                  d3       t        j.                  |d4| j>                  | j@                  -      j                  t        j&                  #       t        j"                  |d5| jB                        j                  t        j&                  d2!       t        j                  |d6      }|j                  t        j                  d3       t        j                  |d7      j                  ddddd8       t        jD                  |dd9| jF                  d8:      j                  dddd8d8       t        j                  |d;      j                  ddddd8       t        jD                  |d<d=| jH                  d8:      j                  dd$dd8d8       t        j                  |d>      j                  dd?ddd8       t        jD                  |dd@dA| jJ                  dB      j                  dddd8d8       t        j.                  |dC| jL                  D      j                  dd8ddEd8       t        j"                  |dF| jN                        j                  ddGddd8       t        j                  |dH      }|j                  t        j                  d3       t        j.                  |dI| jP                  D      j                  ddddd       t        j                  |dJ      j                  dddKd?d       t        jR                  || jT                  dLM      j                  ddd$dd?d&       t        j"                  |dN| jV                        j                  dddd8d       t        j                  |dO      j                  dddKd?d       t        jD                  |ddP| jX                  d8:      j                  dddd?d       t        j                  |dQ      j                  dddKd?d       t        jD                  |dRdSdT| jZ                  dGB      j                  dd$dd?d       t        j                  |dU      j                  dd?dKd?d       t        jD                  |dTdVdT| j\                  dGB      j                  dddd?d       t        j                  |dW      j                  dddKd?d       t        jD                  |dXdYdT| j^                  dGB      j                  dddd?d       t        j                  |d>      j                  dddKd?d       t        jD                  |dd@dA| j`                  dB      j                  dd$dd?d       t        j                  |dZ      j                  dd?dKd?d       t        jD                  |d<d=| jb                  dG:      j                  dddd?d       t        j"                  |d[| jd                        j                  d$ddddd\&       t        j                  |d]d^d_`      j                  d$dd?dd?d\&       |j+                  dd)       t        j                  |      }	|	j                  t        j                  d3       t        j"                  |	da| jf                        j                  t        j&                  d !       t        j"                  |	db| jh                        j                  t        j&                  d !       t        j"                  |	dc| jj                        j                  t        j&                  d !       t        j"                  |	dd| jl                        j                  t        j&                  d !       t        j"                  |	ded$| jn                  f      j                  t        jp                  dg!       t        j                  |dh      }
|
j                  t        j                  d3       t        j                  |
di      j                  dddjdd       t        j                  |
| jr                  d^dkl      j                  ddddd       t        j                  |
dm      j                  dddjdd       t        j                  |
| jt                  d^dkl      j                  ddddd       |
j+                  dd)       t        j                  |      }|j                  t        j                  d3       t        j"                  |dn| jv                        j                  t        j&                  d !       t        j"                  |do| jx                        j                  t        j&                  d !       t        j                  || jz                  *      j                  ddp+       t        j                  |dqdrds`      j                  dd2+       y )tNr  )paddingTr   u   원격 화면 대상)zSegoe UI
   bold)rW   fontr  )anchorzRemote Desktop Source)rW   )r   r  )r   padyu   1. 전체 바탕화면r  )rW   r  variablecommandr         )rowcolumnstickypadxr  readonlyB   )ri  r   z<<ComboboxSelected>>u   2. 디스플레이r  r5   wer   u   디스플레이 새로고침)rW   r  )r      )sider   u   모니터 진단)r%     )r   r$  )r  r  
columnspanr  r   r  uP   고급/보기용: 선택 창(HWND) 캡처 - 원격제어 메인 용도는 아님r  )weight)textvariable)r  r  ul   선택 창 고정(고급 창 캡처용) - 원격 데스크톱 사용 중에는 전체/디스플레이 권장)rW   r  r  )r   r   uc   원격 제어 허용 - viewer에서 클릭/우클릭/더블클릭/휠을 실제 PC 좌표로 실행uO   파일전송 허용 - viewer에서 보낸 파일을 recv_files 폴더에 저장u   수신 폴더 열기)r  r   )r   r  u;   클립보드 수동 전송 허용 - 자동 동기화 없음u)   현재 host 클립보드 viewer로 전송u   전송 옵션FPSr$     )from_tor)  r   u   JPEG 품질r&  rc  rZ  r   i      )r,  r-  	incrementr)  r   u   원격 화면 빨간 테두리)rW   r  r  u   옵션 적용   u,   AI 스냅샷 / Google Drive 동기화 폴더u"   변화 있을 때 스냅샷 저장u   폴더e&   )r)  r   u   폴더 열기u   최근개수r  u	   확인초r   g      N@rr  u   저장간격초g     r@u
   변화율%r   g      Y@u   품질u   스냅샷 설정 저장+적용)r   r  u   Google Drive for Desktop 동기화 폴더를 지정하면 Drive 앱이 업로드를 담당합니다. API 업로드는 별도 단계입니다.z#555555i  )rW   
foreground
wraplengthu   서버+캡처 시작u   캡처 중지u   로컬 viewer 열기u   LAN URL 복사.)rW   r   r  )r$  r   u   주소zLAN:nwi  )r)  r3  r4  u   공유:u   UPnP 열고 공유 URL 복사u   UPnP 닫기)r   r   u   주의: UPnP 공유는 외부 노출입니다. 테스트가 끝나면 원격 제어 OFF + 파일전송 OFF + 클립보드 OFF + UPnP 닫기를 누르세요.z#aa0000i  )>r   Framer   r   r   r   Label
LabelFrameXRadiobuttonr  r  gridComboboxr  rp   r  Buttonr
  LEFTr  columnconfigurer+  Checkbuttonr  on_lock_changedr  r  r  on_file_transfer_changedopen_recv_dirr  on_clipboard_changedsend_host_clipboard_nowSpinboxr2  r3  r4  r1  apply_capture_optionsr9  Entryr;  open_ai_snapshot_folderr=  rA  rC  rE  rF  rG  save_apply_ai_snapshot_settingsr  stop_captureopen_local_viewercopy_lan_urlhide_to_trayRIGHTr5  r6  start_upnp_and_copy_share_url
close_upnpr  )r   r   src_box	diag_btnsadvancedfile_rowclip_rowopt_boxai_boxbtnsurl_box	upnp_btnss               r,   rI  zWin2RtcApp.setup_ui  s^   		$))R0


-		%4;STYYadYe..-DE"$$W-&>iZ^ZiZisw  tJ  tJ  	K  	P  	P  UV  _`  il  st  {|  	P  	} \\'2N 68P8PQ&:)VZVeVeos  pF  pF  	G  	L  	L  QR  [\  eh  op  wx  	L  	yAa11MIIg&	1QsC

9#A4KeKefkkqsqxqx  @Fk  	G

9#5t?T?TUZZ`b`g`gZh99W%!A!DqvVc__**	
 $BGG$
!,""$		%dmm49969R]]((		

 $c$
'v%%++		

 $c$
'99U#244f-b++11		

 $BGG$


8"8$BTBTUZZ`b`g`gnuZv99U#244g.N''--		

 $BGG$


8"MW[WsWstyy  @B  @G  @G  NUy  	V.._="$$W-		'&++!CaVW+XG1$,,aPUUZ[denqxy  ABU  	C		'.33!CVW^_3`G2"4;K;KSTUZZ_`ijsv}~  FGZ  	H		',11a#TU\]1^G14K]K]efgllqr{|  FI  PQ  XYl  	Z&FQUQ`Q`affkluv  @C  JL  STf  	U

7$:T:TUZZ_`ijsv}~  FGZ  	H,Z[G,%ITXThThinnst}~  HK  RS  Z[n  	\		&x(--!AcPQXY-Z		&t'B'B"MRRWXabopy}  EF  MNR  	O

69U9UV[[`ajktw~  GH[  	I		&~.33!CVW^_3`F!$:S:S[\]bbghqr{~  FG  NOb  	P		&{+00QqST[\0]F#$#DLiLiqrsxx}~  HI  RU  \]  dex  	f		&01661QsYZab6cF#%3TMnMnvwx}}  CD  MN  WZ  ab  ij}  	k		&|,11a#TU\]1^F#%3TMhMhpqrww|}  GH  QT  [\  cdw  	e		&{+00QqST[\0]F!$JhJhpqrww|}  GH  QT  [\  cdw  	e		&x(--!AcPQXY-ZF"$:V:V^_`eejktu  B  IJ  QRe  	S

6 @$JnJnottyz  DE  RS  \_  fg  ntt  	u		&   o  |E  RU  	V  	[  	[  `a  jk  xy  BE  LM  TZ  	[  	[q+yy		rtt'	*

44d>P>PQVV\^\c\cjpVq

4ot7H7HINNTVT[T[bhNi

44d>T>TUZZ`b`g`gntZu

4.8I8IJOOUWU\U\ciOj

4cD4E4EFKKQSQYQY`fKg..X6"$$W-		'',,1TPQXY,Z		'(:(:y]`affkluv  @C  JK  RSf  	T		'	*//AaST[\/]		'(:(:y]`affkluv  @C  JK  RSf  	T!,IIe$	BDDw/

9#BDLnLnottz|  {B  {B  IOt  	P

9=$//JOOUWU\U\ciOj		%doo6;;3V;T		 p 		

 $c$
(r.   c                   t        t        | j                  j                               t	        | j
                  j                         xs d      t        dt        | j                  j                                     t        dt        | j                  j                                     t        dt        | j                  j                                     t        dt        | j                  j                                     t        dt        | j                  j                                     t        dt        dt        | j                  j                                           	      S )
Nsnapshots_driver5   r   rr  r   r   r&  rc  )r8  r:  r<  r@  rB  rD  rZ  rY  )r   r   r9  r[   rZ   r;  rd  ri   r=  r?  rA  rC  rE  rF  re  rG  r   s    r,   rr  z%Win2RtcApp.build_ai_snapshot_settings^  s    --1134t22668M<MN1c$";";"?"?"ABCS%(E(E(I(I(K"LM"%c51R1R1V1V1X+Y"Z%(eD4O4O4S4S4U.V%W!S!?!?!C!C!EFGRRT-I-I-M-M-O)P!QR	
 		
r.   c           	     *   | j                         }|| j                  _        | j                  j	                  |       | j                          | j                  j                  d|j                  rdnd d|j                   d|j                   d       y )Nu#   AI 스냅샷 설정 저장+적용: r  r  r  u
    / 최근 u   개)rr  r  r7  r)  rK  ru  r  r   r8  r:  r<  )r   r  s     r,   rK  z*Win2RtcApp.save_apply_ai_snapshot_settingsj  s    ,,.%'"**2.&&(A"**$Z_A``cdfdmdmcnnxy{  zG  zG  yH  HK  L  	Mr.   c                   | j                         }t        |j                        j                         }|j	                         st        j
                         |z  }	 |j                  dd       t        j                  t        |             y # t        $ r(}| j                  j                  d|        Y d }~y d }~ww xY w)NTr:   u#   AI 스냅샷 폴더 열기 실패: )rr  r   r:  
expanduseris_absoluter  rI   r  	startfilerZ   rG   r  r   )r   r  r:  rV   s       r,   rJ  z"Win2RtcApp.open_ai_snapshot_folderq  s    ,,.bii++-!!#XXZ&(F	MLLL5LLV% 	MOO"EcU KLL	Ms   1B 	B?B::B?c                N   | j                   j                         }|dk(  rF| j                  j                          | j                  j                  d       | j                          y |dk(  r`| j                  j                          | j                  j                          | j                  j                  d       | j                          y |dk(  rV| j                          | j                  j                          | j                  j                  d       | j                          y y )Nr  uJ   고급 선택 창 캡처 모드: 창 위에 마우스를 2초 멈추세요r  u   전체 바탕화면 모드r  u   디스플레이 모드)r  r[   rW  
set_windowr  r   rz  rJ  r   r   r  )r   rx  s     r,   r  zWin2RtcApp.on_source_changed|  s    ""$8**,OO lm'')Y++-LL%%'OO <='')Y$$&LL%%'OO 89'')	 r.   c                L   t        d| j                  j                               }| j                  j	                  |       | j
                  j                  |       | j                  j	                  d       | j                  j	                  d| d       | j                          y )Nr   r  r  u    선택)
rd  r  r  r/  r   rW  r  r  r  rz  )r   r~  r  s      r,   r  zWin2RtcApp.on_display_selected  s    !T''//12S!'',I&.se7;<##%r.   c                L   | j                         }| j                  j                         rO|r1t        j                  |      r| j
                  j                  d       n7| j
                  j                  d       n| j
                  j                  d       | j                          y )NuA   선택 창 고정됨. 다른 창으로 자동 변경되지 않음u%   고정할 선택 창이 아직 없음u   선택 창 고정 해제됨)r  r  r[   r   r   r  r   rz  )r   r   s     r,   rB  zWin2RtcApp.on_lock_changed  su    %%'==))$/##$gh##$KLOO =>##%r.   c                    | j                   j                         r,| j                  j                  d       | j	                          y | j                  j                  d       | j	                          y )Nun   원격 제어 허용됨. 공유 URL 사용자는 viewer에서 제어 ON 후 실제 클릭할 수 있습니다.u   원격 제어 차단됨)r  r[   r  r   rz  r   s    r,   r  zWin2RtcApp.on_control_changed  sX    !OO  !Q  R'')OO 9:'')r.   c                   | j                   j                         rl| j                  j                  dd       | j                  j                  d| j                          | j                  ddddd       | j                          y | j                  j                  d	       | j                  ddd
dd       | j                          y )NTr:   u'   파일전송 허용됨. 수신 폴더: notifyfiler8  u   파일전송 허용)r  kindri  filenameu   파일전송 차단됨disabledu   파일전송 차단)r  r[   r  rI   r  r   r  rz  r   s    r,   rC  z#Win2RtcApp.on_file_transfer_changed  s    !!%%'MMt<OO"I$-- YZ6Icx yz'')OO 896Jdy z{'')r.   c                2   t        | j                  j                               }| j                  j	                  |       |r,| j
                  j                  d       | j                          y | j
                  j                  d       | j                          y )NuN   클립보드 수동 전송 허용됨. 자동 동기화는 하지 않습니다.u$   클립보드 수동 전송 차단됨)r   r  r[   r!  notify_stater  r   rz  )r   r8  s     r,   rE  zWin2RtcApp.on_clipboard_changed  sp    t))--/0((1OO pq'')OO FG'')r.   c                    | j                   j                         s| j                  j                  d       y | j                  j                  t        j                  t        t              d             | j                  j                  d       y )Nu;   먼저 클립보드 수동 전송 허용을 체크하세요Fr  u?   현재 host 클립보드를 viewer로 전송 요청했습니다)
r  r[   r  r   r!  r  r  r  r   r   r   s    r,   rF  z"Win2RtcApp.send_host_clipboard_now  sc    !!%%'OO ]^//

8DU;Vej0kl]^r.   c                    	 | j                   j                  dd       t        j                  t	        | j                                y # t
        $ r(}| j                  j                  d|        Y d }~y d }~ww xY w)NTr:   u   수신 폴더 열기 실패: )r  rI   r  rc  rZ   rG   r  r   r7  s     r,   rD  zWin2RtcApp.open_recv_dir  sc    	GMMt<LLT]]+, 	GOO"?u EFF	Gs   AA 	A9A44A9c                    |r:t        j                  |      r%| j                  j                  d| d|d d         y | j                  j                  d       y )Nu   선택된 창: r  r  r  )r   r   r+  r   r   s      r,   rH  zWin2RtcApp.on_hwnd_change  sJ    H%%d+MMvSsEFMM56r.   c           
        | j                   j                  | j                  j                         | j                  j                         | j
                  j                         | j                  j                                | j                  j                  d| j                   j                   d| j                   j                   d| j                   j                   d| j                   j                          | j                          y )Nr  u   옵션 적용: fps=z
, quality=z, max_width=z	, border=)r(  rf  r2  r[   r3  r4  r1  r  r   rX  rY  rZ  r[  rz  r   s    r,   rH  z Win2RtcApp.apply_capture_options  s   **  "))--/((,,.++-	 	+ 	
 	!$"5"5"9"9!:*TEXEXEeEeDffrsw  tG  tG  tQ  tQ  sR  R[  \`  \o  \o  \{  \{  [|  }	
 	##%r.   c                V    | j                   j                  | j                                y r   )r5  r   r  r   s    r,   rP  z"Win2RtcApp.refresh_local_url_label  s    t6689r.   c                   | j                          | j                  j                          | j                  j                          | j                  j                  j                  d       | j                  j                          | j                          | j                  j                  d       | j                  dd       | j                  d       y )Ng      ?r  u-   실행 중: 원격 화면 viewer에서 확인r  zpreview startedpreview_started)rH  r'  r   r%  r   r  r(  rP  r  r   r  rQ  r   s    r,   r  zWin2RtcApp.start_preview  s    ""$ ""'''4!!#$$&KLe%67*+r.   c                    | j                   j                          | j                  j                  d       | j	                  dd       | j                  d       y )Nu-   캡처 중지됨. HTTP/WS 서버는 유지됨r  zcapture stoppedcapture_stopped)r(  r   r  r   r  rQ  r   s    r,   rL  zWin2RtcApp.stop_capture  sG      "KLe%67*+r.   c                    | j                          t        j                  | j                                | j	                  dd       | j                  d       y )Nr  zlocal viewer openedviewer_opened)r  r  r  r  r  rQ  r   s    r,   rM  zWin2RtcApp.open_local_viewer  sB    1134e%:;)r.   c                    | j                   j                          | j                   j                  |       | j                   j                          y r   )r   clipboard_clearclipboard_appendupdater>  s     r,   	copy_textzWin2RtcApp.copy_text  s4    		!!#		""4(		r.   c                   | j                          | j                         }| j                  |       | j                  j	                  |       | j
                  j	                  d       | j                  dd       | j                  d       y )Nu4   LAN viewer URL을 클립보드에 복사했습니다r  zLAN viewer URL copied
url_copied)r  r  r  r5  r   r  r  rQ  )r   r  s     r,   rN  zWin2RtcApp.copy_lan_url  sn    %%'ss#RSe%<=&r.   c                    | j                          | j                  j                  d       t        j                  | j
                  d      j                          y )Nu   UPnP 실행 중...Tr   )r  r  r   r   r   _upnp_workerr   r   s    r,   rQ  z(Win2RtcApp.start_upnp_and_copy_share_url  s@    01 1 1$?EEGr.   c                     j                   j                   j                   j                  j                   j
                  j                   j                  j                        j                  rwj                  xs j                  }|s j                  }t        |j                  |j                   j                         j                  j!                  d fd       y  j                  j!                  d fd       y )N)r  r&  r"  public_ip_urlr   c                 <    j                   j                        S r   )_finish_upnp_okr   )r  r   	share_urls   r,   r   z)Win2RtcApp._upnp_worker.<locals>.<lambda>  s    t';';FNNI'Vr.   c                 :    j                   j                        S r   )_finish_upnp_failr   )r  r   s   r,   r   z)Win2RtcApp._upnp_worker.<locals>.<lambda>  s    t'='=fnn'Mr.   )r  
open_portsr  r'  rv   r%  r  r  r   echo_public_iprouter_external_ipr   http_external_portws_external_portr8   r   r  )r   public_hostr  r  s   ` @@r,   r  zWin2RtcApp._upnp_worker
  s    %%;;&&++NN''))11	 & 
 99 //L63L3LK"kk'))''

I IIOOAVWIIOOAMNr.   c                    | j                   j                  |       | j                  |       | j                  j                  | d       y )Nu    / 공유 URL 복사 완료)r6  r   r  r  )r   r   r  s      r,   r  zWin2RtcApp._finish_upnp_ok   s<    y)y!wi'BCDr.   c                p    | j                   j                  d       | j                  j                  |       y )Nu   UPnP 실패)r6  r   r  )r   r   s     r,   r  zWin2RtcApp._finish_upnp_fail%  s(    }-G$r.   c                n    | j                   j                         }| j                  j                  |       y r   )r  close_portsr  r   )r   r  s     r,   rR  zWin2RtcApp.close_upnp)  s&    ii##%C r.   c                F   	 | j                   | j                   j                         sOd| _        | j                  j	                          | j                  j                          | j                  ddd       yd| _        | j                  j                          | j                  dd       y# t        $ rl}d| _        	 | j                  j	                          | j                  j                          n# t        $ r Y nw xY w| j                  dd| d       Y d}~yd}~ww xY w)	u`   실행 직후 RTC 메인창을 Tray에 숨긴다. Tray 생성 실패 시 화면을 복구한다.NFr  uD   [TRAY-01][체크실패] 시작 Tray 생성 실패 / 메인창 표시r   Tu1   [TRAY-01][체크완료] 시작 즉시 Tray 숨김u3   [TRAY-01][체크실패] 시작 Tray 숨김 실패: )	r  r  r  r   	deiconifyliftr  withdrawrG   r7  s     r,   rR  z"Win2RtcApp.hide_to_tray_on_startup-  s    	oyy 		(8#( 		##%		 %%e-su|}#DII !!%)\] 	o$D		##%		  !!%+^_b^c)dfmnn	osB   A4B+ 73B+ +	D 4D<4C10D1	C=:D<C==DD c                   	 | j                   | j                   j                         s/| j                  j                  d       | j	                  ddd       y| j                          d| _        | j                  j                          | j	                  dd       y# t        $ r }| j	                  dd| d       Y d}~yd}~ww xY w)	u-   RTC 메인창을 시스템 Tray로 숨긴다.Nu8   Tray 아이콘 생성 실패 - 화면을 유지합니다r  uD   [TRAY-02][체크실패] Tray 아이콘 생성 실패 / 화면 유지r   Tu6   [TRAY-02][체크완료] 메인창 숨김 / Tray 이동u,   [TRAY-02][체크실패] Tray 이동 실패: )
r  r  r  r   r  ru  r  r   r  rG   r7  s     r,   rO  zWin2RtcApp.hide_to_trayB  s    
	hyy 		(8##$^_%%e-su|}**,#DII !!%)ab 	h!!%+WX[W\)]_fgg	hs   AB AB 	C$B??Cc                    	  j                    j                   j                          d _         j                  j	                           j                  j                          	  j                  j                  dd        j                  j                  d fd        j                  j                           j                  dd       y# t        $ r Y 8w xY w# t        $ r } j                  dd	| d
       Y d}~yd}~ww xY w)u8   Tray 아이콘 클릭 시 RTC 메인창을 복원한다.NFr   T   c                 <     j                   j                  dd      S )Nr   F)r   r   r   s   r,   r   z.Win2RtcApp.restore_from_tray.<locals>.<lambda>Z  s    TYY-A-A*e-Tr.   r  u3   [TRAY-03][체크완료] Tray에서 메인창 복원u,   [TRAY-03][체크실패] Tray 복원 실패: r   )r  hider  r   r  r  r   r  rG   update_idletasksr  r7  s   ` r,   restore_from_trayzWin2RtcApp.restore_from_trayP  s    	hyy$		 $DII!IINN		$$Z6		%TU II&&(!!%)^_    	h!!%+WX[W\)]_fgg	hs<   A!C %;C  ,C 	CC CC 	D%D  Dc                   	 | j                   *| j                   j                         r| j                          	 | j                  j	                         r'| j                  j                  d| j                         y y # t        $ r Y y w xY w# 	 | j                  j	                         r'| j                  j                  d| j                         w w # t        $ r Y w w xY wxY w)Nr  )r  poll_restorer  r   winfo_existsr  rS  rG   r   s    r,   rS  zWin2RtcApp.poll_tray_restoreb  s    	yy$)?)?)A&&(99))+IIOOC)?)?@ , 99))+IIOOC)?)?@ , sB   6B
 A A; ;	BB
CA CC	CCCCc                   | j                          	 | j                  | j                  j                          | j                  j                          	 | j
                  j                          	 | j                  j                          | j                  j                          | j                  j                          | j                  j                          | j                  j                          | j                  dd       | j                  d       | j                  j                          t!        j"                  d       y # t        $ r Y w xY w# t        $ r Y w xY w# t        $ r Y w xY w)Nr  zmain window closedclosedr   )ru  r  r   rG   r(  r)  r!  r  r   r   r  r  r'  r  rQ  r   rX  exitr   s    r,   rM  zWin2RtcApp.on_closem  s'   &&(	yy$		  	  "	##%	$$&((002 			e%9:"		%  		
  		
  		s5   &E E .4E" 	EE	EE"	E.-E.r+   r   r   r   )r   r   )r   r  )r  rZ   r   r   )ri  rZ   r   r  )r   r   )i  )ry  ri   r   r  r   )rM   )r  rZ   r   rZ   r  rZ   r   r  )r   rw  )r   zdict | Noner   rw  )r6   )r  rZ   r   r  )r   r%   r   )r   rZ   )r  rw  )r   rZ   )r  rw  )r  ri   r  rw  r   rZ   )r   r   rG  )r   rZ   r  rZ   )Br   r   r   r   staticmethodr  r  r  rh  rg  rp  rs  ru  rz  rx  rN  rO  r  r  r  r  rQ  rU  rT  r  r  r  r  r  r  r$  r  r  r   r
  r  rI  rr  rK  rJ  r  r  rB  r  rC  rE  rF  rD  rH  rH  rP  r  rL  rM  r  rN  rQ  r  r  r  rR  rR  rO  r  rS  rM  r~   r.   r,   r  r  8  s   e9P 3 3M a a
8;
	-)1
f4>_*	$L ! !7Fk
f
?@$$-L*& 
 
S*A)B

M	M*"&	&*	**_G7
&:	,,*
'H
O,E
%!o*hh$	r.   r  c                   t        j                  d      }|j                  dddd       |j                  dd	d
       |j                  dt        dd       |j                  dt        dd       |j                  dt        dd       |j                  dt        dd       |j                  dt        dd       |j                  ddd       |j                  ddd d!"       |j                  d#t         j                  d$d%&       |j                  d'd(d)       |j                  d*d+d,       |j                  d-dd.       |j                  d/g d0d1d23       |j                  d4g d5d+d63       |j                  d7g d8d+d93       |j                  d:t        d;d<       |j                  d=dd>       |j                  |       S )?Nz)yjm_win2rtc Windows Remote Desktop Bridge)descriptionz-vz	--verbose
store_trueu   상세 로그)r  r  z--hostrf   u<   서버 바인딩 주소. 외부/LAN 공유는 0.0.0.0 권장)defaultr  z--http-portiV"  u   viewer.html HTTP 포트)r  r  r  z	--ws-portiW"  u   JPEG frame WebSocket 포트z--fpsr  u
   캡처 FPSz	--qualityr  u   JPEG 품질 1~95z--max-widthr  u2   전송 전 최대 가로폭. 0이면 원본 크기z--no-auto-portu;   포트 사용 중일 때 자동 다음 포트 탐색을 끔z
--no-tokenr8   store_falseu    viewer URL 토큰 사용 안 함)destr  r  z--borderTu2   전송 프레임 자체에 빨간 테두리 표시)r  r  r  z--public-ip-urlzhttp://i.yjm.kr/ip/u   공인 IP 확인 URLz--state-filer6   u*   WS가 읽을 RTC 상태 JSON 파일 경로z--auto-startu6   실행 직후 HTTP/WS/capture preview를 자동 시작z--viewer)manualr  r  r  u*   auto-start 후 viewer 자동 열기 여부)choicesr  r  z	--control)r  r  r6   u=   원격 제어 허용 초기값. 생략하면 저장값 사용z--source)r  r  r  r6   u:   자동 시작 캡처 대상. 생략하면 저장값 사용z--display-indexr   u6   source=display일 때 UI 기준 디스플레이 번호z--dump-displaysu4   디스플레이 감지 결과만 출력하고 종료)argparseArgumentParseradd_argumentri   BooleanOptionalAction
parse_args)rY  parsers     r,   r  r    s   $$1\]F
k,_U
):xy
CD]^
#tB_`
c2LI
#r@RS
CDxy
(  EB  C
7=Oqr

8+I+ISW  _S  T
)3HOef
9ef
|Bz{

,Dh  ^J  K
->  RQ  R

,PZ\  d`  a
)QE}~
),E{|T""r.   c                   t        |       }|j                  rEt        t        j                               D ]$  \  }}t        t        j                  ||             & yt        t        dt               t        d       yt        j                         }|j                          t        ||       |j                          y)Nr   u0   [ERROR] websockets 패키지를 확인하세요:u5   설치 예: python -m pip install -r requirements.txtr   )r  dump_displaysr  r   list_displaysprintr  r   r"   r-  r   Tkr  mainloop)rY  r  r  r  r   s        r,   mainr    s    dD 4 B B DEFC*11#q9: F}@BZ[EF557DMMOtTMMOr.   __main__r  )TN)rN   r   rO   r   r   
list[dict])rQ   r  r   	list[str])rb   rZ   rc   r  r   r   )<   )rr   rZ   rs   ri   rt   ri   r   ri   r   )ir   
__future__r   r  r	  r  r  r   r  rJ   rj   r  rX  r   r   r  	functoolsr   http.serverr   r   pathlibr   urllib.parser   r	   urllib.requestr  urllib.errorr
  r)   r*   ST_ROOTrZ   rN   insertr  Shared.windows_trayr   rG   tkinterr   r   rs  PILr   r   r   r  yjm_capture_sourcesr   yjm_monitor_prober   yjm_net_upnpr   r   r  r   yjm_remote_statusr   yjm_file_transferr   yjm_clipboard_syncr   host.protocolr   r   r   r   yjm_settingsr   r   r   r   r    yjm_ai_snapshot_ringr!   websockets.asyncio.serverr"   websockets.datastructuresr#   websockets.http11r$   r-  rV   r&   __annotations__Lockr   r  r  r-   rX   r_   rd   rx   rz   r   rI  rT  r  r  r  r   
SystemExitr~   r.   r,   <module>r     s   #   	   	    
     E  +  
x.
 
 
"
)
)
0
0w<sxxHHOOAs7|$8#h.1GG3   
     4 / 8 4 3 8 3 _ _ ` ` 3
$/1*  $ !z  INN		3
DyJ JZP, P,f BT; T;nL L^"#.& z
TV
 G4  O4  #EGH"	#s*   G ?G  GG G7%G22G7