diff --git a/browser_ext/background.js b/browser_ext/background.js
index 84ce20e15..068866de2 100644
--- a/browser_ext/background.js
+++ b/browser_ext/background.js
@@ -8,6 +8,9 @@ const ignoredPages = {
"directory": true,
};
+let popup = chrome.extension.getViews({type: "popup"})[0];
+popup.window.getElementById("status").innerHTML = "NaM";
+
/// return channel name if it should contain a chat
function matchChannelName(url) {
if (!url)
@@ -43,6 +46,7 @@ function getPort() {
function connectPort() {
port = chrome.runtime.connectNative(appName);
console.log("port connected");
+ let connected = true;
port.onMessage.addListener(function (msg) {
console.log(msg);
@@ -52,6 +56,14 @@ function connectPort() {
port = null;
});
+
+ let sendPing = () => {
+ if (connected) {
+ port.postMessage({ ping: true });
+ } else {
+ setTimeout(sendPing, 5000);
+ }
+ }
}
@@ -63,6 +75,12 @@ chrome.tabs.onActivated.addListener((activeInfo) => {
chrome.windows.get(tab.windowId, {}, (window) => {
if (!window.focused) return;
+ if (window.state == "fullscreen") {
+ tryDetach(tab.windowId);
+ return;
+ }
+
+ console.log("onActivated");
onTabSelected(tab.url, tab);
});
});
@@ -73,16 +91,27 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (!tab.highlighted)
return;
- onTabSelected(changeInfo.url, tab);
+ chrome.windows.get(tab.windowId, {}, (window) => {
+ if (!window.focused) return;
+ if (window.state == "fullscreen") {
+ tryDetach(tab.windowId);
+ return;
+ }
+
+ console.log("onUpdated");
+ onTabSelected(tab.url, tab);
+ });
});
// tab detached
chrome.tabs.onDetached.addListener((tabId, detachInfo) => {
+ console.log("onDetached");
tryDetach(detachInfo.oldWindowId);
});
// tab closed
chrome.windows.onRemoved.addListener((windowId) => {
+ console.log("onRemoved");
tryDetach(windowId);
});
@@ -96,13 +125,20 @@ chrome.windows.onFocusChanged.addListener((windowId) => {
if (tabs.length === 1) {
let tab = tabs[0];
- onTabSelected(tab.url, tab);
+ chrome.windows.get(tab.windowId, (window) => {
+ if (window.state == "fullscreen") {
+ tryDetach(tab.windowId);
+ return;
+ }
+
+ console.log("onFocusChanged");
+ onTabSelected(tab.url, tab);
+ });
}
});
});
-
// attach or detach from tab
function onTabSelected(url, tab) {
let channelName = matchChannelName(url);
@@ -131,6 +167,10 @@ chrome.runtime.onMessage.addListener((message, sender, callback) => {
// is window focused
chrome.windows.get(sender.tab.windowId, {}, (window) => {
if (!window.focused) return;
+ if (window.state == "fullscreen") {
+ tryDetach(sender.tab.windowId);
+ return;
+ }
// get zoom value
chrome.tabs.getZoom(sender.tab.id, (zoom) => {
@@ -169,6 +209,8 @@ function tryAttach(windowId, data) {
function tryDetach(windowId) {
let port = getPort();
+ console.log("tryDetach");
+
if (port) {
port.postMessage({
action: "detach",
diff --git a/browser_ext/inject.js b/browser_ext/inject.js
index 812b6580f..29d132817 100644
--- a/browser_ext/inject.js
+++ b/browser_ext/inject.js
@@ -2,72 +2,9 @@
let lastRect = {};
let port = null;
- function log(str) {
- console.log("Chatterino Native: " + str);
- }
-
- function findChatDiv() {
- return document.getElementsByClassName("right-column")[0];
- }
-
- function queryChatRect() {
- if (!matchChannelName(window.location.href)) return;
-
- let element = findChatDiv();
-
- if (element === undefined) {
- log("failed to find chat div");
- return;
- }
-
- if (!element.chatterino) {
- let xd = element.getElementsByClassName("channel-page__right-column")[0]
-
- if (xd != undefined) {
- element.chatterino = true;
- xd.style.opacity = 0;
-
- setTimeout(() => {
- xd.innerHTML = "
" +
- "Disconnected from the chatterino extension.
Please refresh the page." +
- "
";
- xd.style.opacity = 1;
- }, 2000);
- }
- }
-
- let rect = element.getBoundingClientRect();
-
- // if (
- // lastRect.left == rect.left &&
- // lastRect.right == rect.right &&
- // lastRect.top == rect.top &&
- // lastRect.bottom == rect.bottom
- // ) {
- // // log("skipped sending message");
- // return;
- // }
- lastRect = rect;
-
- let data = {
- rect: rect,
- };
-
- try {
- chrome.runtime.sendMessage(data);
- } catch {
- // failed to send a message to the runtime -> maybe the extension got reloaded
- // alert("reload the page to re-enable chatterino native");
- }
- }
-
- function queryCharRectLoop() {
- let t1 = performance.now();
- queryChatRect();
- let t2 = performance.now();
- console.log("queryCharRect " + (t2 - t1) + "ms");
- // setTimeout(queryCharRectLoop, 500);
- }
+ let installedObjects = {};
+ let rightCollapseButton = null;
+ let isCollapsed = false;
const ignoredPages = {
"settings": true,
@@ -79,7 +16,126 @@
"directory": true,
};
- /// return channel name if it should contain a chat
+ let findChatDiv = () => document.getElementsByClassName("right-column")[0];
+ let findRightCollapse = () => document.getElementsByClassName("right-column__toggle-visibility")[0];
+ let findRightColumn = () => document.getElementsByClassName("channel-page__right-column")[0];
+ let findNavBar = () => document.getElementsByClassName("top-nav__menu")[0];
+
+ // logging function
+ function log(str) {
+ console.log("Chatterino Native: " + str);
+ }
+
+ // install events
+ function installChatterino() {
+ log("trying to install events");
+
+ let retry = false;
+
+ // right collapse button
+ if (!installedObjects.rightCollapse) {
+ retry = true;
+
+ let x = findRightCollapse();
+
+ if (x != undefined) {
+ rightCollapseButton = x;
+
+ x.addEventListener("mouseup", () => {
+ let y = findChatDiv();
+
+ if (parseInt(y.style.width) == 0) {
+ y.style.width = "340px";
+ isCollapsed = false;
+ } else {
+ y.style.width = 0;
+ isCollapsed = true;
+ }
+ });
+
+ installedObjects.rightCollapse = true;
+ }
+ }
+
+ // right column
+ if (!installedObjects.rightColumn && installedObjects.rightCollapse) {
+ let x = findChatDiv();
+
+ if (x != undefined && x.children.length >= 2) {
+ x.children[0].innerHTML = "" +
+ "Disconnected from the chatterino extension.
Please focus the window or refresh the page." +
+ "
";
+
+ installedObjects.rightColumn = true;
+ } else {
+ retry = true;
+ }
+ }
+
+ // nav bar
+ if (rightCollapseButton && !installedObjects.topNav) {
+ let x = findRightCollapse();
+
+ installedObjects.topNav = true;
+ } else {
+ retry = true;
+ }
+
+ // retry if needed
+ if (retry) {
+ setTimeout(installChatterino, 1000);
+ } else {
+ log("installed all events");
+ }
+ }
+
+ // query the rect of the chat
+ function queryChatRect() {
+ if (!matchChannelName(window.location.href)) return;
+
+ let element = findChatDiv();
+
+ if (element === undefined) {
+ log("failed to find chat div");
+ return;
+ }
+
+ let rect = element.getBoundingClientRect();
+
+ /* if (
+ lastRect.left == rect.left &&
+ lastRect.right == rect.right &&
+ lastRect.top == rect.top &&
+ lastRect.bottom == rect.bottom
+ ) {
+ // log("skipped sending message");
+ return;
+ } */
+ lastRect = rect;
+
+ let data = {
+ rect: rect,
+ };
+
+ isCollapsed = rect.width == 0;
+
+ try {
+ chrome.runtime.sendMessage(data);
+ } catch {
+ // failed to send a message to the runtime -> maybe the extension got reloaded
+ // alert("reload the page to re-enable chatterino native");
+ }
+ }
+
+ function queryChatRectLoop() {
+ let t1 = performance.now();
+ queryChatRect();
+ let t2 = performance.now();
+ console.log("queryCharRect " + (t2 - t1) + "ms");
+ // setTimeout(queryCharRectLoop, 500);
+ }
+
+ // return channel name if it should contain a chat or undefined
function matchChannelName(url) {
if (!url)
return undefined;
@@ -94,15 +150,20 @@
return undefined;
}
- queryCharRectLoop();
- window.addEventListener("load", () => {
- setTimeout(queryChatRect, 1000);
- });
+
+ // event listeners
+ window.addEventListener("load", () => setTimeout(queryChatRect, 1000));
window.addEventListener("resize", queryChatRect);
window.addEventListener("focus", queryChatRect);
- window.addEventListener("mouseup", () => {
- setTimeout(queryChatRect, 10);
+ window.addEventListener("mouseup", () => setTimeout(queryChatRect, 10));
+ window.addEventListener("hashchange", () => {
+ installedObjects = {};
+ installChatterino();
});
- log("initialized");
+ //
+ log("hello there in the dev tools 👋");
+
+ queryChatRectLoop();
+ installChatterino();
})()
diff --git a/browser_ext/popup.html b/browser_ext/popup.html
index e24ef44ab..221fc73e6 100644
--- a/browser_ext/popup.html
+++ b/browser_ext/popup.html
@@ -1,6 +1,10 @@
+
- xd
+
+ Chatterino extension :)
+
+
diff --git a/lib/settings b/lib/settings
index 439b58597..ad31b3886 160000
--- a/lib/settings
+++ b/lib/settings
@@ -1 +1 @@
-Subproject commit 439b585976a864de679fc5d5b2c688380b2ef72c
+Subproject commit ad31b38866d80a17ced902476ed06da69edce3a0
diff --git a/src/main.cpp b/src/main.cpp
index ab8bfc622..6a8e85f5d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -84,10 +84,10 @@ int runGui(int argc, char *argv[])
qApp->setPalette(darkPalette);
-// Install native event handler for hidpi on windows
-#ifdef USEWINSDK
- a.installNativeEventFilter(new chatterino::util::DpiNativeEventFilter);
-#endif
+ // Install native event handler for hidpi on windows
+ //#ifdef USEWINSDK
+ // a.installNativeEventFilter(new chatterino::util::DpiNativeEventFilter);
+ //#endif
// Initialize NetworkManager
chatterino::util::NetworkManager::init();
@@ -149,6 +149,17 @@ void runNativeMessagingHost()
bool bigEndian = isBigEndian();
#endif
+ std::atomic ping(false);
+
+ QTimer timer;
+ QObject::connect(&timer, &QTimer::timeout, [&ping] {
+ if (!ping.exchange(false)) {
+ _exit(0);
+ }
+ });
+ timer.setInterval(11000);
+ timer.start();
+
while (true) {
char size_c[4];
std::cin.read(size_c, 4);
diff --git a/src/singletons/nativemessagingmanager.cpp b/src/singletons/nativemessagingmanager.cpp
index a4810fddc..f817a91a0 100644
--- a/src/singletons/nativemessagingmanager.cpp
+++ b/src/singletons/nativemessagingmanager.cpp
@@ -195,7 +195,10 @@ void NativeMessagingManager::ReceiverThread::handleMessage(const QJsonObject &ro
}
#ifdef USEWINSDK
- util::postToThread([winId] { widgets::AttachedWindow::detach(winId); });
+ util::postToThread([winId] {
+ qDebug() << "NW detach";
+ widgets::AttachedWindow::detach(winId);
+ });
#endif
} else {
qDebug() << "NM unknown action " + action;
diff --git a/src/widgets/attachedwindow.cpp b/src/widgets/attachedwindow.cpp
index d28facfb8..8738e4c26 100644
--- a/src/widgets/attachedwindow.cpp
+++ b/src/widgets/attachedwindow.cpp
@@ -1,15 +1,15 @@
#include "attachedwindow.hpp"
#include "application.hpp"
+#include "util/debugcount.hpp"
+#include "widgets/split.hpp"
#include
#include
-#include "widgets/split.hpp"
-
#ifdef USEWINSDK
#include "Windows.h"
-
+// don't even think about reordering these
#include "Psapi.h"
#pragma comment(lib, "Dwmapi.lib")
#endif
@@ -19,17 +19,19 @@ namespace widgets {
AttachedWindow::AttachedWindow(void *_target, int _yOffset)
: QWidget(nullptr, Qt::FramelessWindowHint | Qt::Window)
- , target(_target)
- , yOffset(_yOffset)
+ , target_(_target)
+ , yOffset_(_yOffset)
{
QLayout *layout = new QVBoxLayout(this);
layout->setMargin(0);
this->setLayout(layout);
auto *split = new Split(this);
- this->ui.split = split;
+ this->ui_.split = split;
split->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
layout->addWidget(split);
+
+ util::DebugCount::increase("attached window");
}
AttachedWindow::~AttachedWindow()
@@ -40,6 +42,8 @@ AttachedWindow::~AttachedWindow()
break;
}
}
+
+ util::DebugCount::decrease("attached window");
}
AttachedWindow *AttachedWindow::get(void *target, const GetArgs &args)
@@ -64,7 +68,7 @@ AttachedWindow *AttachedWindow::get(void *target, const GetArgs &args)
window->hide();
show = false;
} else {
- window->_height = args.height;
+ window->height_ = args.height;
size.setHeight(args.height);
}
}
@@ -73,14 +77,14 @@ AttachedWindow *AttachedWindow::get(void *target, const GetArgs &args)
window->hide();
show = false;
} else {
- window->_width = args.width;
+ window->width_ = args.width;
size.setWidth(args.width);
}
}
if (show) {
+ window->updateWindowRect_(window->target_);
window->show();
- // window->resize(size);
}
return window;
@@ -97,80 +101,93 @@ void AttachedWindow::detach(const QString &winId)
void AttachedWindow::setChannel(ChannelPtr channel)
{
- this->ui.split->setChannel(channel);
+ this->ui_.split->setChannel(channel);
}
void AttachedWindow::showEvent(QShowEvent *)
{
- attachToHwnd(this->target);
+ attachToHwnd_(this->target_);
}
-void AttachedWindow::attachToHwnd(void *_hwnd)
+void AttachedWindow::attachToHwnd_(void *_attachedPtr)
{
#ifdef USEWINSDK
- QTimer *timer = new QTimer(this);
- timer->setInterval(1);
+ if (this->attached_) {
+ return;
+ }
- HWND hwnd = HWND(this->winId());
- HWND attached = HWND(_hwnd);
+ this->attached_ = true;
+ this->timer_.setInterval(1);
- QObject::connect(timer, &QTimer::timeout, [this, hwnd, attached, timer] {
+ auto hwnd = HWND(this->winId());
+ auto attached = HWND(_attachedPtr);
+ QObject::connect(&this->timer_, &QTimer::timeout, [this, hwnd, attached] {
// check process id
- DWORD processId;
- ::GetWindowThreadProcessId(attached, &processId);
+ if (!this->validProcessName_) {
+ DWORD processId;
+ ::GetWindowThreadProcessId(attached, &processId);
- HANDLE process =
- ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processId);
+ HANDLE process =
+ ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processId);
- std::unique_ptr filename(new TCHAR[512]);
- DWORD filenameLength = ::GetModuleFileNameEx(process, nullptr, filename.get(), 512);
- QString qfilename = QString::fromWCharArray(filename.get(), filenameLength);
+ std::unique_ptr filename(new TCHAR[512]);
+ DWORD filenameLength = ::GetModuleFileNameEx(process, nullptr, filename.get(), 512);
+ QString qfilename = QString::fromWCharArray(filename.get(), filenameLength);
- if (!qfilename.endsWith("chrome.exe")) {
- qDebug() << "NM Illegal callee" << qfilename;
- timer->stop();
- timer->deleteLater();
- this->deleteLater();
- return;
+ if (!qfilename.endsWith("chrome.exe")) {
+ qDebug() << "NM Illegal caller" << qfilename;
+ this->timer_.stop();
+ this->deleteLater();
+ return;
+ }
+ this->validProcessName_ = true;
}
- // We get the window rect first so we can close this window when it returns an error.
- // If we query the process first and check the filename then it will return and empty string
- // that doens't match.
- ::SetLastError(0);
- RECT rect;
- ::GetWindowRect(attached, &rect);
-
- if (::GetLastError() != 0) {
- timer->stop();
- timer->deleteLater();
- this->deleteLater();
- return;
- }
-
- // set the correct z-order
- HWND next = ::GetNextWindow(attached, GW_HWNDPREV);
-
- ::SetWindowPos(hwnd, next ? next : HWND_TOPMOST, 0, 0, 0, 0,
- SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
-
- if (this->_height == -1) {
- // ::MoveWindow(hwnd, rect.right - this->_width - 8, rect.top + this->yOffset
- // - 8,
- // this->_width, rect.bottom - rect.top - this->yOffset, false);
- } else {
- ::MoveWindow(hwnd, rect.right - this->_width - 8, rect.bottom - this->_height - 8,
- this->_width, this->_height, false);
- }
-
- // ::MoveWindow(hwnd, rect.right - 360, rect.top + 82, 360 - 8, rect.bottom -
- // rect.top - 82 - 8, false);
+ this->updateWindowRect_(attached);
});
- timer->start();
+ this->timer_.start();
#endif
}
+void AttachedWindow::updateWindowRect_(void *_attachedPtr)
+{
+ auto hwnd = HWND(this->winId());
+ auto attached = HWND(_attachedPtr);
+
+ // We get the window rect first so we can close this window when it returns an error.
+ // If we query the process first and check the filename then it will return and empty string
+ // that doens't match.
+ ::SetLastError(0);
+ RECT rect;
+ ::GetWindowRect(attached, &rect);
+
+ if (::GetLastError() != 0) {
+ qDebug() << "NM GetLastError()" << ::GetLastError();
+
+ this->timer_.stop();
+ this->deleteLater();
+ return;
+ }
+
+ // set the correct z-order
+ HWND next = ::GetNextWindow(attached, GW_HWNDPREV);
+
+ ::SetWindowPos(hwnd, next ? next : HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+
+ if (this->height_ == -1) {
+ // ::MoveWindow(hwnd, rect.right - this->width_ - 8, rect.top + this->yOffset_ - 8,
+ // this->width_, rect.bottom - rect.top - this->yOffset_, false);
+ } else {
+ ::MoveWindow(hwnd, rect.right - this->width_ - 8, rect.bottom - this->height_ - 8,
+ this->width_, this->height_, false);
+ }
+
+ // ::MoveWindow(hwnd, rect.right - 360, rect.top + 82, 360 - 8, rect.bottom -
+ // rect.top - 82 - 8, false);
+}
+
// void AttachedWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
//{
// MSG *msg = reinterpret_cast
diff --git a/src/widgets/attachedwindow.hpp b/src/widgets/attachedwindow.hpp
index 8c8dd6bf8..8d7c216cd 100644
--- a/src/widgets/attachedwindow.hpp
+++ b/src/widgets/attachedwindow.hpp
@@ -10,7 +10,7 @@ namespace widgets {
class AttachedWindow : public QWidget
{
- AttachedWindow(void *target, int asdf);
+ AttachedWindow(void *target_, int asdf);
public:
struct GetArgs {
@@ -20,9 +20,9 @@ public:
int height = -1;
};
- ~AttachedWindow();
+ virtual ~AttachedWindow() override;
- static AttachedWindow *get(void *target, const GetArgs &args);
+ static AttachedWindow *get(void *target_, const GetArgs &args);
static void detach(const QString &winId);
void setChannel(ChannelPtr channel);
@@ -33,17 +33,21 @@ protected:
// override;
private:
- void *target;
- int yOffset;
- int currentYOffset;
- int _width = 360;
- int _height = -1;
+ void *target_;
+ int yOffset_;
+ int currentYOffset_;
+ int width_ = 360;
+ int height_ = -1;
+ bool validProcessName_ = false;
+ bool attached_ = false;
+ QTimer timer_;
struct {
Split *split;
- } ui;
+ } ui_;
- void attachToHwnd(void *hwnd);
+ void attachToHwnd_(void *attached);
+ void updateWindowRect_(void *attached);
struct Item {
void *hwnd;