mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
fix WebSocket deadlock on slow client + mobile TX controls visibility
Deadlock: BroadcastBinary held FClientLock during blocking SockSend; slow/stalled client caused Stop() to wait forever. Fix: set SO_SNDTIMEO=1s on each accepted socket so SockSend returns error instead of hanging, mark client wsClosed on send failure so subsequent ticks skip it. Mobile: move TONE/TX-slider/MOX to front of toolbar via CSS order so they are immediately visible without horizontal scroll on phones. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+39
-2
@@ -97,6 +97,36 @@ begin
|
||||
'.wf-area canvas{position:absolute;top:0;left:0;width:100%;height:100%}' +
|
||||
'.sbar{flex:0 0 16px;display:flex;align-items:center;padding:0 6px;gap:10px;background:#0e0e0e;border-top:1px solid #222;font-size:10px;color:#555}' +
|
||||
'#latEl{margin-left:auto}' +
|
||||
'@media screen and (max-width:640px){' +
|
||||
// Тулбар 1: горизонтальный скролл вместо переноса
|
||||
'.tbar:not(.row2){overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;}' +
|
||||
'.tbar:not(.row2)::-webkit-scrollbar{display:none;}' +
|
||||
'.tbar:not(.row2)>*{flex-shrink:0;}' +
|
||||
// Крупнее зоны касания
|
||||
'button{height:30px;}select{height:30px;}' +
|
||||
// VFO-строка: стекаем smeter / vfo-mid / right-g каждый на своей строке
|
||||
'.row2{height:auto;flex-wrap:wrap;padding:3px;gap:3px;}' +
|
||||
'#smeter{width:100%;height:40px;}' +
|
||||
'.vfo-mid{width:100%;gap:2px;justify-content:flex-start;}' +
|
||||
'.vfo-ro{font-size:20px;height:34px;padding:0 2px;padding-top:6px;}' +
|
||||
'.vfo-ro .dg,.vfo-ro .sp{font-size:20px;}' +
|
||||
'.vfo-hz .dg{font-size:10px;}' +
|
||||
'.vbtn{width:26px;height:26px;font-size:10px;}' +
|
||||
'.swap-g button{height:22px;padding:0 4px;font-size:9px;}' +
|
||||
'.vfo-sp{display:none;}' +
|
||||
'.right-g{width:100%;margin-left:0;}' +
|
||||
'.right-g button,.right-g select{height:28px;font-size:11px;}' +
|
||||
// Водопад короче на телефоне
|
||||
'.wf-area{flex:0 0 180px;}' +
|
||||
// Слайдеры чуть уже
|
||||
'input[type=range]{width:55px;}' +
|
||||
// TX+MOX пинним в начало тулбара чтобы были видны без скролла
|
||||
'#toneBtn{order:-3;}' +
|
||||
'.tx-g{order:-2;}' +
|
||||
'#moxBtn{order:-1;min-width:52px;height:30px;font-weight:bold;}' +
|
||||
// iOS Safari: 100vh включает адресную строку — используем реальную высоту
|
||||
'body,.wrap{height:-webkit-fill-available;}' +
|
||||
'}' +
|
||||
'</style></head>' +
|
||||
|
||||
'<body><div class="wrap">' +
|
||||
@@ -110,7 +140,7 @@ begin
|
||||
'<span class="sl-sep">|</span>' +
|
||||
'<button id="toneBtn">TONE</button>' +
|
||||
'<span class="sl-sep">|</span>' +
|
||||
'<div class="sl-g"><span class="sl-lb">TX</span><input type="range" id="txSl" min="0" max="100" value="50"><span class="sl-val" id="txVal">50</span></div>' +
|
||||
'<div class="sl-g tx-g"><span class="sl-lb">TX</span><input type="range" id="txSl" min="0" max="100" value="50"><span class="sl-val" id="txVal">50</span></div>' +
|
||||
'<button id="moxBtn">MOX</button>' +
|
||||
'<span class="sl-sep">|</span>' +
|
||||
'<div class="tsep"></div>' +
|
||||
@@ -146,7 +176,7 @@ begin
|
||||
'<button id="btnSwap">SWAP</button>' +
|
||||
'<button id="btnAB">A=B</button>' +
|
||||
'</div>' +
|
||||
'<span style="display:inline-block;width:12px"></span>' +
|
||||
'<span class="vfo-sp" style="display:inline-block;width:12px"></span>' +
|
||||
'<button class="vbtn" id="vBtnB">B</button>' +
|
||||
'<div id="vDispB" class="vfo-ro dim"></div>' +
|
||||
'</div>' +
|
||||
@@ -543,6 +573,13 @@ begin
|
||||
'});' +
|
||||
'cv.addEventListener("mouseleave",()=>{cv.style.cursor="";if(!markerOn)hoverX=-1;wTarget="";dragging=false;});}' +
|
||||
'bindCv(specCv);bindCv(wfCv);' +
|
||||
// Touch→mouse мост для канвасов: drag-to-pan и tap-to-tune на мобильных
|
||||
'function addTouch(cv){' +
|
||||
'function mkMs(type,t){return new MouseEvent(type,{bubbles:true,cancelable:true,clientX:t.clientX,clientY:t.clientY});}' +
|
||||
'cv.addEventListener("touchstart",e=>{e.preventDefault();audioKick();cv.dispatchEvent(mkMs("mousedown",e.touches[0]));},{passive:false});' +
|
||||
'cv.addEventListener("touchmove",e=>{e.preventDefault();cv.dispatchEvent(mkMs("mousemove",e.touches[0]));},{passive:false});' +
|
||||
'cv.addEventListener("touchend",e=>{e.preventDefault();cv.dispatchEvent(mkMs("mouseup",e.changedTouches[0]));},{passive:false});}' +
|
||||
'addTouch(specCv);addTouch(wfCv);' +
|
||||
'window.addEventListener("wheel",e=>{if(e.target?.closest?.(".vfo-ro"))return;if(!wTarget){if(e.target===specCv)wTarget="spec";else if(e.target===wfCv)wTarget="wf";}if(wTarget)doWheel(e);},{passive:false,capture:true});' +
|
||||
|
||||
// Opus: async decodeFrame, очередь, оверлей для user gesture
|
||||
|
||||
Reference in New Issue
Block a user