mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
improved chatterino native
This commit is contained in:
parent
cded61d28d
commit
0f8375a2f3
11 changed files with 378 additions and 142 deletions
|
@ -11,10 +11,21 @@ const ignoredPages = {
|
||||||
const appName = "com.chatterino.chatterino";
|
const appName = "com.chatterino.chatterino";
|
||||||
let port = null;
|
let port = null;
|
||||||
|
|
||||||
|
// gets the port for communication with chatterino
|
||||||
|
function getPort() {
|
||||||
|
if (port) {
|
||||||
|
return port;
|
||||||
|
} else {
|
||||||
|
// TODO: add cooldown
|
||||||
|
connectPort();
|
||||||
|
|
||||||
/// Connect to port
|
return port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// connect to port
|
||||||
function connectPort() {
|
function connectPort() {
|
||||||
port = chrome.runtime.connectNative("com.chatterino.chatterino");
|
port = chrome.runtime.connectNative(appName);
|
||||||
console.log("port connected");
|
console.log("port connected");
|
||||||
|
|
||||||
port.onMessage.addListener(function (msg) {
|
port.onMessage.addListener(function (msg) {
|
||||||
|
@ -27,76 +38,147 @@ function connectPort() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPort() {
|
|
||||||
if (port) {
|
|
||||||
return port;
|
|
||||||
} else {
|
|
||||||
// TODO: add cooldown
|
|
||||||
connectPort();
|
|
||||||
|
|
||||||
return port;
|
// tab activated
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Tab listeners
|
|
||||||
chrome.tabs.onActivated.addListener((activeInfo) => {
|
chrome.tabs.onActivated.addListener((activeInfo) => {
|
||||||
|
console.log(0)
|
||||||
chrome.tabs.get(activeInfo.tabId, (tab) => {
|
chrome.tabs.get(activeInfo.tabId, (tab) => {
|
||||||
if (!tab)
|
console.log(1)
|
||||||
return;
|
if (!tab || !tab.url) return;
|
||||||
|
|
||||||
if (!tab.url)
|
console.log(2)
|
||||||
return;
|
chrome.windows.get(tab.windowId, {}, (window) => {
|
||||||
|
if (!window.focused) return;
|
||||||
|
console.log(3)
|
||||||
|
|
||||||
matchUrl(tab.url, tab);
|
onTabSelected(tab.url, tab);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// url changed
|
||||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||||
if (!tab.highlighted)
|
if (!tab.highlighted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
matchUrl(changeInfo.url, tab);
|
onTabSelected(changeInfo.url, tab);
|
||||||
|
});
|
||||||
|
|
||||||
|
// tab detached
|
||||||
|
chrome.tabs.onDetached.addListener((tabId, detachInfo) => {
|
||||||
|
tryDetach(detachInfo.oldWindowId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// tab closed
|
||||||
|
chrome.windows.onRemoved.addListener((windowId) => {
|
||||||
|
tryDetach(windowId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// window selected
|
||||||
|
chrome.windows.onFocusChanged.addListener((windowId) => {
|
||||||
|
chrome.tabs.query({windowId: windowId, highlighted: true}, (tabs) => {
|
||||||
|
if (tabs.length >= 1) {
|
||||||
|
let tab = tabs[0];
|
||||||
|
|
||||||
|
onTabSelected(tab.url, tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/// Misc
|
/// return channel name if it should contain a chat
|
||||||
function matchUrl(url, tab) {
|
function matchChannelName(url) {
|
||||||
if (!url)
|
if (!url)
|
||||||
return;
|
return undefined;
|
||||||
|
|
||||||
const match = url.match(/^https?:\/\/(www\.)?twitch.tv\/([a-zA-Z0-9]+)\/?$/);
|
const match = url.match(/^https?:\/\/(www\.)?twitch.tv\/([a-zA-Z0-9_]+)\/?$/);
|
||||||
|
|
||||||
let channelName;
|
let channelName;
|
||||||
|
|
||||||
console.log(tab);
|
|
||||||
|
|
||||||
if (match && (channelName = match[2], !ignoredPages[channelName])) {
|
if (match && (channelName = match[2], !ignoredPages[channelName])) {
|
||||||
console.log("channelName " + channelName);
|
return channelName;
|
||||||
console.log("winId " + tab.windowId);
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// attach or detach from tab
|
||||||
|
function onTabSelected(url, tab) {
|
||||||
|
let channelName = matchChannelName(url);
|
||||||
|
|
||||||
|
if (channelName) {
|
||||||
chrome.windows.get(tab.windowId, {}, (window) => {
|
chrome.windows.get(tab.windowId, {}, (window) => {
|
||||||
let yOffset = window.height - tab.height;
|
// attach to window
|
||||||
|
tryAttach(tab.windowId, {
|
||||||
let port = getPort();
|
name: channelName,
|
||||||
if (port) {
|
yOffset: window.height - tab.height,
|
||||||
port.postMessage({
|
});
|
||||||
action: "select",
|
|
||||||
attach: true,
|
|
||||||
type: "twitch",
|
|
||||||
name: channelName,
|
|
||||||
winId: "" + tab.windowId,
|
|
||||||
yOffset: yOffset
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let port = getPort();
|
// detach from window
|
||||||
if (port) {
|
tryDetach(tab.windowId);
|
||||||
port.postMessage({
|
}
|
||||||
action: "detach",
|
}
|
||||||
winId: "" + tab.windowId
|
|
||||||
})
|
// receiving messages from the inject script
|
||||||
}
|
function registerTheGarbage() {
|
||||||
|
chrome.runtime.onMessage.addListener((message, sender, callback) => {
|
||||||
|
// is tab highlighted
|
||||||
|
if (!sender.tab.highlighted) return;
|
||||||
|
|
||||||
|
// is window focused
|
||||||
|
chrome.windows.get(sender.tab.windowId, {}, (window) => {
|
||||||
|
if (!window.focused) return;
|
||||||
|
|
||||||
|
// get zoom value
|
||||||
|
chrome.tabs.getZoom(sender.tab.id, (zoom) => {
|
||||||
|
let size = {
|
||||||
|
width: message.rect.width * zoom,
|
||||||
|
height: message.rect.height * zoom,
|
||||||
|
};
|
||||||
|
|
||||||
|
// attach to window
|
||||||
|
tryAttach(sender.tab.windowId, {
|
||||||
|
name: matchChannelName(sender.tab.url),
|
||||||
|
size: size,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerLoop() {
|
||||||
|
// loop until the runtime objects exists because I can't be arsed to figure out the proper way to do this
|
||||||
|
if (chrome.runtime === undefined) {
|
||||||
|
setTimeout(registerLoop(), 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerTheGarbage();
|
||||||
|
}
|
||||||
|
registerLoop();
|
||||||
|
|
||||||
|
// attach chatterino to a chrome window
|
||||||
|
function tryAttach(windowId, data) {
|
||||||
|
data.action = "select";
|
||||||
|
data.attach = true;
|
||||||
|
data.type = "twitch";
|
||||||
|
data.winId = "" + windowId;
|
||||||
|
|
||||||
|
let port = getPort();
|
||||||
|
|
||||||
|
if (port) {
|
||||||
|
port.postMessage(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// detach chatterino from a chrome window
|
||||||
|
function tryDetach(windowId) {
|
||||||
|
let port = getPort();
|
||||||
|
|
||||||
|
if (port) {
|
||||||
|
port.postMessage({
|
||||||
|
action: "detach",
|
||||||
|
winId: "" + windowId
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
57
browser_ext/inject.js
Normal file
57
browser_ext/inject.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
(() => {
|
||||||
|
let lastRect = {};
|
||||||
|
|
||||||
|
function log(str) {
|
||||||
|
console.log("Chatterino Native: " + str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findChatDiv() {
|
||||||
|
return document.getElementsByClassName("right-column")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryChatRect() {
|
||||||
|
let element = findChatDiv();
|
||||||
|
|
||||||
|
if (element === undefined) {
|
||||||
|
log("failed to find chat div");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// element.firstChild.style.opacity = 0;
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
chrome.runtime.sendMessage(data, (response) => {
|
||||||
|
// log("received message response");
|
||||||
|
// console.log(response)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryCharRectLoop() {
|
||||||
|
let t1 = performance.now();
|
||||||
|
queryChatRect();
|
||||||
|
let t2 = performance.now();
|
||||||
|
console.log("queryCharRect " + (t2 - t1) + "ms");
|
||||||
|
setTimeout(queryCharRectLoop, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
queryCharRectLoop();
|
||||||
|
window.addEventListener("resize", queryChatRect);
|
||||||
|
|
||||||
|
log("initialized");
|
||||||
|
})()
|
|
@ -3,7 +3,8 @@
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"description": "xd",
|
"description": "xd",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"tabs", "nativeMessaging"
|
"tabs",
|
||||||
|
"nativeMessaging"
|
||||||
],
|
],
|
||||||
"icons": {
|
"icons": {
|
||||||
"256": "icon.png"
|
"256": "icon.png"
|
||||||
|
@ -17,5 +18,16 @@
|
||||||
},
|
},
|
||||||
"browser_action": {
|
"browser_action": {
|
||||||
"default_popup": "popup.html"
|
"default_popup": "popup.html"
|
||||||
}
|
},
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"run_at": "document_end",
|
||||||
|
"matches": [
|
||||||
|
"https://www.twitch.tv/*"
|
||||||
|
],
|
||||||
|
"js": [
|
||||||
|
"inject.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,6 @@
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <io.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace chatterino::singletons;
|
using namespace chatterino::singletons;
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
@ -234,52 +228,6 @@ void Application::save()
|
||||||
this->commands->save();
|
this->commands->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::runNativeMessagingHost()
|
|
||||||
{
|
|
||||||
auto app = getApp();
|
|
||||||
|
|
||||||
app->nativeMessaging = new singletons::NativeMessagingManager;
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
_setmode(_fileno(stdin), _O_BINARY);
|
|
||||||
_setmode(_fileno(stdout), _O_BINARY);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
bool bigEndian = isBigEndian();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
char size_c[4];
|
|
||||||
std::cin.read(size_c, 4);
|
|
||||||
|
|
||||||
if (std::cin.eof()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t size = *reinterpret_cast<uint32_t *>(size_c);
|
|
||||||
#if 0
|
|
||||||
// To avoid breaking strict-aliasing rules and potentially inducing undefined behaviour, the following code can be run instead
|
|
||||||
uint32_t size = 0;
|
|
||||||
if (bigEndian) {
|
|
||||||
size = size_c[3] | static_cast<uint32_t>(size_c[2]) << 8 |
|
|
||||||
static_cast<uint32_t>(size_c[1]) << 16 | static_cast<uint32_t>(size_c[0]) << 24;
|
|
||||||
} else {
|
|
||||||
size = size_c[0] | static_cast<uint32_t>(size_c[1]) << 8 |
|
|
||||||
static_cast<uint32_t>(size_c[2]) << 16 | static_cast<uint32_t>(size_c[3]) << 24;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char *b = reinterpret_cast<char *>(malloc(size + 1));
|
|
||||||
std::cin.read(b, size);
|
|
||||||
*(b + size) = '\0';
|
|
||||||
|
|
||||||
app->nativeMessaging->sendToGuiProcess(QByteArray(b, static_cast<int32_t>(size)));
|
|
||||||
|
|
||||||
free(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Application *getApp()
|
Application *getApp()
|
||||||
{
|
{
|
||||||
assert(staticApp != nullptr);
|
assert(staticApp != nullptr);
|
||||||
|
|
|
@ -22,9 +22,8 @@ std::shared_ptr<Account> AccountModel::getItemFromRow(std::vector<QStandardItem
|
||||||
void AccountModel::getRowFromItem(const std::shared_ptr<Account> &item,
|
void AccountModel::getRowFromItem(const std::shared_ptr<Account> &item,
|
||||||
std::vector<QStandardItem *> &row)
|
std::vector<QStandardItem *> &row)
|
||||||
{
|
{
|
||||||
row[0]->setData(item->toString(), Qt::DisplayRole);
|
util::setStringItem(row[0], item->toString(), false);
|
||||||
row[0]->setData(QFont("Segoe UI", 10), Qt::FontRole);
|
row[0]->setData(QFont("Segoe UI", 10), Qt::FontRole);
|
||||||
// row[0]->setData(QColor(255, 255, 255), Qt::BackgroundRole);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int AccountModel::beforeInsert(const std::shared_ptr<Account> &item,
|
int AccountModel::beforeInsert(const std::shared_ptr<Account> &item,
|
||||||
|
@ -34,8 +33,6 @@ int AccountModel::beforeInsert(const std::shared_ptr<Account> &item,
|
||||||
auto row = this->createRow();
|
auto row = this->createRow();
|
||||||
|
|
||||||
util::setStringItem(row[0], item->getCategory(), false, false);
|
util::setStringItem(row[0], item->getCategory(), false, false);
|
||||||
// row[0]->setData(QColor(142, 36, 170), Qt::ForegroundRole);
|
|
||||||
// row[0]->setData(QColor(255, 255, 255), Qt::BackgroundRole);
|
|
||||||
row[0]->setData(QFont("Segoe UI Light", 16), Qt::FontRole);
|
row[0]->setData(QFont("Segoe UI Light", 16), Qt::FontRole);
|
||||||
|
|
||||||
this->insertCustomRow(std::move(row), proposedIndex);
|
this->insertCustomRow(std::move(row), proposedIndex);
|
||||||
|
|
55
src/main.cpp
55
src/main.cpp
|
@ -18,7 +18,14 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
int runGui(int argc, char *argv[]);
|
int runGui(int argc, char *argv[]);
|
||||||
|
void runNativeMessagingHost();
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
@ -31,11 +38,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
// TODO: can be any argument
|
// TODO: can be any argument
|
||||||
if (args.size() > 0 && args[0].startsWith("chrome-extension://")) {
|
if (args.size() > 0 && args[0].startsWith("chrome-extension://")) {
|
||||||
chatterino::Application::instantiate(argc, argv);
|
runNativeMessagingHost();
|
||||||
auto app = chatterino::getApp();
|
|
||||||
app->construct();
|
|
||||||
|
|
||||||
chatterino::Application::runNativeMessagingHost();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,3 +135,45 @@ int runGui(int argc, char *argv[])
|
||||||
|
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void runNativeMessagingHost()
|
||||||
|
{
|
||||||
|
auto *nm = new chatterino::singletons::NativeMessagingManager;
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
_setmode(_fileno(stdin), _O_BINARY);
|
||||||
|
_setmode(_fileno(stdout), _O_BINARY);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
bool bigEndian = isBigEndian();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
char size_c[4];
|
||||||
|
std::cin.read(size_c, 4);
|
||||||
|
|
||||||
|
if (std::cin.eof()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t size = *reinterpret_cast<uint32_t *>(size_c);
|
||||||
|
#if 0
|
||||||
|
// To avoid breaking strict-aliasing rules and potentially inducing undefined behaviour, the following code can be run instead
|
||||||
|
uint32_t size = 0;
|
||||||
|
if (bigEndian) {
|
||||||
|
size = size_c[3] | static_cast<uint32_t>(size_c[2]) << 8 |
|
||||||
|
static_cast<uint32_t>(size_c[1]) << 16 | static_cast<uint32_t>(size_c[0]) << 24;
|
||||||
|
} else {
|
||||||
|
size = size_c[0] | static_cast<uint32_t>(size_c[1]) << 8 |
|
||||||
|
static_cast<uint32_t>(size_c[2]) << 16 | static_cast<uint32_t>(size_c[3]) << 24;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::unique_ptr<char[]> b(new char[size + 1]);
|
||||||
|
std::cin.read(b.get(), size);
|
||||||
|
*(b.get() + size) = '\0';
|
||||||
|
|
||||||
|
nm->sendToGuiProcess(QByteArray::fromRawData(b.get(), static_cast<int32_t>(size)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -50,6 +50,10 @@ void NativeMessagingManager::registerHost()
|
||||||
{
|
{
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
|
|
||||||
|
if (app->paths->isPortable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// create manifest
|
// create manifest
|
||||||
QJsonDocument document;
|
QJsonDocument document;
|
||||||
QJsonObject root_obj;
|
QJsonObject root_obj;
|
||||||
|
@ -144,26 +148,33 @@ void NativeMessagingManager::ReceiverThread::handleMessage(const QJsonObject &ro
|
||||||
QString _type = root.value("type").toString();
|
QString _type = root.value("type").toString();
|
||||||
bool attach = root.value("attach").toBool();
|
bool attach = root.value("attach").toBool();
|
||||||
QString name = root.value("name").toString();
|
QString name = root.value("name").toString();
|
||||||
QString winId = root.value("winId").toString();
|
|
||||||
int yOffset = root.value("yOffset").toInt(-1);
|
|
||||||
|
|
||||||
if (_type.isNull() || name.isNull() || winId.isNull()) {
|
widgets::AttachedWindow::GetArgs args;
|
||||||
|
args.winId = root.value("winId").toString();
|
||||||
|
args.yOffset = root.value("yOffset").toInt(-1);
|
||||||
|
args.width = root.value("size").toObject().value("width").toInt(-1);
|
||||||
|
args.height = root.value("size").toObject().value("height").toInt(-1);
|
||||||
|
|
||||||
|
if (_type.isNull() || args.winId.isNull()) {
|
||||||
qDebug() << "NM type, name or winId missing";
|
qDebug() << "NM type, name or winId missing";
|
||||||
attach = false;
|
attach = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_type == "twitch") {
|
if (_type == "twitch") {
|
||||||
util::postToThread([name, attach, winId, yOffset, app] {
|
util::postToThread([=] {
|
||||||
app->twitch.server->watchingChannel.update(
|
if (!name.isEmpty()) {
|
||||||
app->twitch.server->getOrAddChannel(name));
|
app->twitch.server->watchingChannel.update(
|
||||||
|
app->twitch.server->getOrAddChannel(name));
|
||||||
|
}
|
||||||
|
|
||||||
if (attach) {
|
if (attach) {
|
||||||
#ifdef USEWINSDK
|
#ifdef USEWINSDK
|
||||||
auto *window =
|
auto *window = widgets::AttachedWindow::get(::GetForegroundWindow(), args);
|
||||||
widgets::AttachedWindow::get(::GetForegroundWindow(), winId, yOffset);
|
if (!name.isEmpty()) {
|
||||||
window->setChannel(app->twitch.server->getOrAddChannel(name));
|
window->setChannel(app->twitch.server->getOrAddChannel(name));
|
||||||
window->show();
|
}
|
||||||
|
// window->show();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -185,7 +196,7 @@ void NativeMessagingManager::ReceiverThread::handleMessage(const QJsonObject &ro
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "NM unknown action " + action;
|
qDebug() << "NM unknown action " + action;
|
||||||
}
|
}
|
||||||
}
|
} // namespace singletons
|
||||||
|
|
||||||
} // namespace singletons
|
} // namespace singletons
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -19,21 +19,21 @@ PathManager::PathManager(int argc, char **argv)
|
||||||
.replace("/", "x");
|
.replace("/", "x");
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
bool portable = false;
|
this->portable = false;
|
||||||
|
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
if (strcmp(argv[i], "portable") == 0) {
|
if (strcmp(argv[i], "portable") == 0) {
|
||||||
portable = true;
|
this->portable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (QFileInfo::exists(QCoreApplication::applicationDirPath() + "/portable")) {
|
if (QFileInfo::exists(QCoreApplication::applicationDirPath() + "/this->portable")) {
|
||||||
portable = true;
|
this->portable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root path = %APPDATA%/Chatterino or the folder that the executable resides in
|
// Root path = %APPDATA%/Chatterino or the folder that the executable resides in
|
||||||
QString rootPath;
|
QString rootPath;
|
||||||
if (portable) {
|
if (this->portable) {
|
||||||
rootPath.append(QCoreApplication::applicationDirPath());
|
rootPath.append(QCoreApplication::applicationDirPath());
|
||||||
} else {
|
} else {
|
||||||
// Get settings path
|
// Get settings path
|
||||||
|
@ -91,5 +91,10 @@ bool PathManager::createFolder(const QString &folderPath)
|
||||||
return QDir().mkpath(folderPath);
|
return QDir().mkpath(folderPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PathManager::isPortable()
|
||||||
|
{
|
||||||
|
return this->portable;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace singletons
|
} // namespace singletons
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -28,6 +28,10 @@ public:
|
||||||
QString appPathHash;
|
QString appPathHash;
|
||||||
|
|
||||||
bool createFolder(const QString &folderPath);
|
bool createFolder(const QString &folderPath);
|
||||||
|
bool isPortable();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool portable;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace singletons
|
} // namespace singletons
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#ifdef USEWINSDK
|
#ifdef USEWINSDK
|
||||||
#include "Windows.h"
|
#include "Windows.h"
|
||||||
|
|
||||||
|
#include "Psapi.h"
|
||||||
#pragma comment(lib, "Dwmapi.lib")
|
#pragma comment(lib, "Dwmapi.lib")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -40,16 +42,47 @@ AttachedWindow::~AttachedWindow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AttachedWindow *AttachedWindow::get(void *target, const QString &winId, int yOffset)
|
AttachedWindow *AttachedWindow::get(void *target, const GetArgs &args)
|
||||||
{
|
{
|
||||||
for (Item &item : items) {
|
AttachedWindow *window = [&]() {
|
||||||
if (item.hwnd == target) {
|
for (Item &item : items) {
|
||||||
return item.window;
|
if (item.hwnd == target) {
|
||||||
|
return item.window;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *window = new AttachedWindow(target, args.yOffset);
|
||||||
|
items.push_back(Item{target, window, args.winId});
|
||||||
|
return window;
|
||||||
|
}();
|
||||||
|
|
||||||
|
bool show = true;
|
||||||
|
QSize size = window->size();
|
||||||
|
|
||||||
|
if (args.height != -1) {
|
||||||
|
if (args.height == 0) {
|
||||||
|
window->hide();
|
||||||
|
show = false;
|
||||||
|
} else {
|
||||||
|
window->_height = args.height;
|
||||||
|
size.setHeight(args.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (args.width != -1) {
|
||||||
|
if (args.width == 0) {
|
||||||
|
window->hide();
|
||||||
|
show = false;
|
||||||
|
} else {
|
||||||
|
window->_width = args.width;
|
||||||
|
size.setWidth(args.width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *window = new AttachedWindow(target, yOffset);
|
if (show) {
|
||||||
items.push_back(Item{target, window, winId});
|
window->show();
|
||||||
|
window->resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,26 +113,58 @@ void AttachedWindow::attachToHwnd(void *_hwnd)
|
||||||
|
|
||||||
HWND hwnd = HWND(this->winId());
|
HWND hwnd = HWND(this->winId());
|
||||||
HWND attached = HWND(_hwnd);
|
HWND attached = HWND(_hwnd);
|
||||||
|
|
||||||
QObject::connect(timer, &QTimer::timeout, [this, hwnd, attached, timer] {
|
QObject::connect(timer, &QTimer::timeout, [this, hwnd, attached, timer] {
|
||||||
|
|
||||||
|
// check process id
|
||||||
|
DWORD processId;
|
||||||
|
::GetWindowThreadProcessId(attached, &processId);
|
||||||
|
|
||||||
|
HANDLE process =
|
||||||
|
::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processId);
|
||||||
|
|
||||||
|
std::unique_ptr<TCHAR[]> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
::SetLastError(0);
|
||||||
RECT xD;
|
RECT rect;
|
||||||
::GetWindowRect(attached, &xD);
|
::GetWindowRect(attached, &rect);
|
||||||
|
|
||||||
if (::GetLastError() != 0) {
|
if (::GetLastError() != 0) {
|
||||||
timer->stop();
|
timer->stop();
|
||||||
timer->deleteLater();
|
timer->deleteLater();
|
||||||
this->deleteLater();
|
this->deleteLater();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set the correct z-order
|
||||||
HWND next = ::GetNextWindow(attached, GW_HWNDPREV);
|
HWND next = ::GetNextWindow(attached, GW_HWNDPREV);
|
||||||
|
|
||||||
::SetWindowPos(hwnd, next ? next : HWND_TOPMOST, 0, 0, 0, 0,
|
::SetWindowPos(hwnd, next ? next : HWND_TOPMOST, 0, 0, 0, 0,
|
||||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||||
::MoveWindow(hwnd, xD.right - 360, xD.top + this->yOffset - 8, 360 - 8,
|
|
||||||
xD.bottom - xD.top - this->yOffset, false);
|
if (this->_height == -1) {
|
||||||
// ::MoveWindow(hwnd, xD.right - 360, xD.top + 82, 360 - 8, xD.bottom - xD.top - 82 -
|
::MoveWindow(hwnd, rect.right - this->_width - 8, rect.top + this->yOffset - 8,
|
||||||
// 8,
|
this->_width, rect.bottom - rect.top - this->yOffset, false);
|
||||||
// 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);
|
||||||
});
|
});
|
||||||
timer->start();
|
timer->start();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,9 +13,16 @@ class AttachedWindow : public QWidget
|
||||||
AttachedWindow(void *target, int asdf);
|
AttachedWindow(void *target, int asdf);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
struct GetArgs {
|
||||||
|
QString winId;
|
||||||
|
int yOffset = -1;
|
||||||
|
int width = -1;
|
||||||
|
int height = -1;
|
||||||
|
};
|
||||||
|
|
||||||
~AttachedWindow();
|
~AttachedWindow();
|
||||||
|
|
||||||
static AttachedWindow *get(void *target, const QString &winId, int yOffset);
|
static AttachedWindow *get(void *target, const GetArgs &args);
|
||||||
static void detach(const QString &winId);
|
static void detach(const QString &winId);
|
||||||
|
|
||||||
void setChannel(ChannelPtr channel);
|
void setChannel(ChannelPtr channel);
|
||||||
|
@ -28,6 +35,9 @@ protected:
|
||||||
private:
|
private:
|
||||||
void *target;
|
void *target;
|
||||||
int yOffset;
|
int yOffset;
|
||||||
|
int currentYOffset;
|
||||||
|
int _width = 360;
|
||||||
|
int _height = -1;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
Split *split;
|
Split *split;
|
||||||
|
|
Loading…
Reference in a new issue