mirror of https://github.com/procxx/kepka.git
merge conflicts fixed
This commit is contained in:
commit
7fd819dc0f
|
@ -25,3 +25,7 @@ ipch/
|
|||
/Mac/
|
||||
/Telegram/*.xcodeproj/xcuserdata/
|
||||
/Telegram/*.xcodeproj/project.xcworkspace/
|
||||
|
||||
/Telegram/*.user.*
|
||||
/Linux/
|
||||
/Telegram/Makefile
|
||||
|
|
|
@ -2,16 +2,18 @@ QT += core
|
|||
|
||||
CONFIG(debug, debug|release) {
|
||||
DEFINES += _DEBUG
|
||||
OBJECTS_DIR = ./../Mac/DebugIntermediateEmoji
|
||||
OBJECTS_DIR = ./../DebugIntermediateEmoji
|
||||
MOC_DIR = ./GeneratedFiles/Debug
|
||||
DESTDIR = ./../Mac/DebugEmoji
|
||||
DESTDIR = ./../DebugEmoji
|
||||
}
|
||||
CONFIG(release, debug|release) {
|
||||
OBJECTS_DIR = ./../Mac/ReleaseIntermediateEmoji
|
||||
OBJECTS_DIR = ./../ReleaseIntermediateEmoji
|
||||
MOC_DIR = ./GeneratedFiles/Release
|
||||
DESTDIR = ./../Mac/ReleaseEmoji
|
||||
DESTDIR = ./../ReleaseEmoji
|
||||
}
|
||||
|
||||
CONFIG += plugin static
|
||||
|
||||
macx {
|
||||
QMAKE_INFO_PLIST = ./SourceFiles/_other/Emoji.plist
|
||||
QMAKE_LFLAGS += -framework Cocoa
|
||||
|
@ -25,7 +27,7 @@ HEADERS += \
|
|||
./SourceFiles/_other/memain.h \
|
||||
./SourceFiles/_other/genemoji.h \
|
||||
|
||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.0/QtGui\
|
||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.0/QtCore\
|
||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.1/QtGui\
|
||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.1/QtCore\
|
||||
./../../Libraries/QtStatic/qtbase/include\
|
||||
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
T += core
|
||||
QT += core
|
||||
|
||||
CONFIG(debug, debug|release) {
|
||||
DEFINES += _DEBUG
|
||||
OBJECTS_DIR = ./../Mac/DebugIntermediateLang
|
||||
OBJECTS_DIR = ./../DebugIntermediateLang
|
||||
MOC_DIR = ./GeneratedFiles/Debug
|
||||
DESTDIR = ./../Mac/DebugLang
|
||||
DESTDIR = ./../DebugLang
|
||||
}
|
||||
CONFIG(release, debug|release) {
|
||||
OBJECTS_DIR = ./../Mac/ReleaseIntermediateLang
|
||||
OBJECTS_DIR = ./../ReleaseIntermediateLang
|
||||
MOC_DIR = ./GeneratedFiles/Release
|
||||
DESTDIR = ./../Mac/ReleaseLang
|
||||
DESTDIR = ./../ReleaseLang
|
||||
}
|
||||
|
||||
CONFIG += plugin static
|
||||
|
||||
macx {
|
||||
QMAKE_INFO_PLIST = ./SourceFiles/_other/Lang.plist
|
||||
QMAKE_LFLAGS += -framework Cocoa
|
||||
|
@ -25,7 +27,7 @@ HEADERS += \
|
|||
./SourceFiles/_other/mlmain.h \
|
||||
./SourceFiles/_other/genlang.h \
|
||||
|
||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.0/QtGui\
|
||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.0/QtCore\
|
||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.1/QtGui\
|
||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.1/QtCore\
|
||||
./../../Libraries/QtStatic/qtbase/include\
|
||||
|
||||
|
|
|
@ -2,16 +2,18 @@ QT += core
|
|||
|
||||
CONFIG(debug, debug|release) {
|
||||
DEFINES += _DEBUG
|
||||
OBJECTS_DIR = ./../Mac/DebugIntermediateStyle
|
||||
OBJECTS_DIR = ./../DebugIntermediateStyle
|
||||
MOC_DIR = ./GeneratedFiles/Debug
|
||||
DESTDIR = ./../Mac/DebugStyle
|
||||
DESTDIR = ./../DebugStyle
|
||||
}
|
||||
CONFIG(release, debug|release) {
|
||||
OBJECTS_DIR = ./../Mac/ReleaseIntermediateStyle
|
||||
OBJECTS_DIR = ./../ReleaseIntermediateStyle
|
||||
MOC_DIR = ./GeneratedFiles/Release
|
||||
DESTDIR = ./../Mac/ReleaseStyle
|
||||
DESTDIR = ./../ReleaseStyle
|
||||
}
|
||||
|
||||
CONFIG += plugin static
|
||||
|
||||
macx {
|
||||
QMAKE_INFO_PLIST = ./SourceFiles/_other/Style.plist
|
||||
QMAKE_LFLAGS += -framework Cocoa
|
||||
|
@ -25,7 +27,7 @@ HEADERS += \
|
|||
./SourceFiles/_other/msmain.h \
|
||||
./SourceFiles/_other/genstyles.h \
|
||||
|
||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.0/QtGui\
|
||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.0/QtCore\
|
||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.1/QtGui\
|
||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.1/QtCore\
|
||||
./../../Libraries/QtStatic/qtbase/include\
|
||||
|
||||
|
|
|
@ -2,14 +2,14 @@ QT += core
|
|||
|
||||
CONFIG(debug, debug|release) {
|
||||
DEFINES += _DEBUG
|
||||
OBJECTS_DIR = ./../Mac/DebugIntermediatePacker
|
||||
OBJECTS_DIR = ./../DebugIntermediatePacker
|
||||
MOC_DIR = ./GeneratedFiles/Debug
|
||||
DESTDIR = ./../Mac/DebugPacker
|
||||
DESTDIR = ./../Debug
|
||||
}
|
||||
CONFIG(release, debug|release) {
|
||||
OBJECTS_DIR = ./../Mac/ReleaseIntermediatePacker
|
||||
OBJECTS_DIR = ./../ReleaseIntermediatePacker
|
||||
MOC_DIR = ./GeneratedFiles/Release
|
||||
DESTDIR = ./../Mac/ReleasePacker
|
||||
DESTDIR = ./../Release
|
||||
}
|
||||
|
||||
macx {
|
||||
|
@ -23,10 +23,10 @@ SOURCES += \
|
|||
HEADERS += \
|
||||
./SourceFiles/_other/packer.h \
|
||||
|
||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.0/QtGui\
|
||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.0/QtCore\
|
||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.1/QtGui\
|
||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.1/QtCore\
|
||||
./../../Libraries/QtStatic/qtbase/include\
|
||||
./../../Libraries/lzma/C
|
||||
|
||||
LIBS += -lcrypto -lssl -lz
|
||||
./../../Libraries/lzma/C\
|
||||
/usr/local/ssl/include
|
||||
|
||||
LIBS += -L/usr/local/ssl/lib -lcrypto -lssl -lz -llzma
|
||||
|
|
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
an unofficial desktop messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
*/
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
|
||||
using std::string;
|
||||
using std::deque;
|
||||
using std::cout;
|
||||
|
||||
bool copyFile(const char *from, const char *to) {
|
||||
FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb");
|
||||
if (!ffrom) {
|
||||
if (fto) fclose(fto);
|
||||
return false;
|
||||
}
|
||||
if (!fto) {
|
||||
fclose(ffrom);
|
||||
return false;
|
||||
}
|
||||
static const int BufSize = 65536;
|
||||
char buf[BufSize];
|
||||
while (size_t size = fread(buf, 1, BufSize, ffrom)) {
|
||||
fwrite(buf, 1, size, fto);
|
||||
}
|
||||
|
||||
struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c
|
||||
//let's say this wont fail since you already worked OK on that fp
|
||||
if (fstat(fileno(ffrom), &fst) != 0) {
|
||||
fclose(ffrom);
|
||||
fclose(fto);
|
||||
return false;
|
||||
}
|
||||
//update to the same uid/gid
|
||||
if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) {
|
||||
fclose(ffrom);
|
||||
fclose(fto);
|
||||
return false;
|
||||
}
|
||||
//update the permissions
|
||||
if (fchmod(fileno(fto), fst.st_mode) != 0) {
|
||||
fclose(ffrom);
|
||||
fclose(fto);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(ffrom);
|
||||
fclose(fto);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove_directory(const string &path) { // from http://stackoverflow.com/questions/2256945/removing-a-non-empty-directory-programmatically-in-c-or-c
|
||||
DIR *d = opendir(path.c_str());
|
||||
if (!d) return false;
|
||||
|
||||
while (struct dirent *p = readdir(d)) {
|
||||
/* Skip the names "." and ".." as we don't want to recurse on them. */
|
||||
if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) continue;
|
||||
|
||||
string fname = path + '/' + p->d_name;
|
||||
struct stat statbuf;
|
||||
if (stat(fname.c_str(), &statbuf) != 0) {
|
||||
if (S_ISDIR(statbuf.st_mode)) {
|
||||
if (remove_directory(fname.c_str())) {
|
||||
closedir(d);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (unlink(fname.c_str())) {
|
||||
closedir(d);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
|
||||
return !rmdir(path.c_str());
|
||||
}
|
||||
|
||||
bool do_mkdir(const char *path) { // from http://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux
|
||||
struct stat statbuf;
|
||||
if (stat(path, &statbuf) != 0) {
|
||||
/* Directory does not exist. EEXIST for race condition */
|
||||
if (mkdir(path, S_IRWXU) != 0 && errno != EEXIST) return false;
|
||||
} else if (!S_ISDIR(statbuf.st_mode)) {
|
||||
errno = ENOTDIR;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mkpath(const char *path) {
|
||||
int status = 0, pathsize = strlen(path) + 1;
|
||||
char *copypath = new char[pathsize];
|
||||
memcpy(copypath, path, pathsize);
|
||||
|
||||
char *pp = copypath, *sp;
|
||||
while (status == 0 && (sp = strchr(pp, '/')) != 0) {
|
||||
if (sp != pp) {
|
||||
/* Neither root nor double slash in path */
|
||||
*sp = '\0';
|
||||
if (!do_mkdir(copypath)) {
|
||||
delete[] copypath;
|
||||
return false;
|
||||
}
|
||||
*sp = '/';
|
||||
}
|
||||
pp = sp + 1;
|
||||
}
|
||||
delete[] copypath;
|
||||
return do_mkdir(path);
|
||||
}
|
||||
|
||||
bool _debug = false;
|
||||
|
||||
string exeName, exeDir;
|
||||
|
||||
bool equal(string a, string b) {
|
||||
std::transform(a.begin(), a.end(), a.begin(), ::tolower);
|
||||
std::transform(b.begin(), b.end(), b.begin(), ::tolower);
|
||||
return a == b;
|
||||
}
|
||||
|
||||
FILE *_logFile = 0;
|
||||
void openLog() {
|
||||
if (!_debug || _logFile) return;
|
||||
|
||||
if (!do_mkdir("DebugLogs")) {
|
||||
return;
|
||||
}
|
||||
|
||||
time_t timer;
|
||||
|
||||
time(&timer);
|
||||
struct tm *t = localtime(&timer);
|
||||
|
||||
static const int maxFileLen = 65536;
|
||||
char logName[maxFileLen];
|
||||
sprintf(logName, "DebugLogs/%04d%02d%02d_%02d%02d%02d_upd.txt",
|
||||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
|
||||
_logFile = fopen(logName, "w");
|
||||
}
|
||||
|
||||
void closeLog() {
|
||||
if (!_logFile) return;
|
||||
|
||||
fclose(_logFile);
|
||||
_logFile = 0;
|
||||
}
|
||||
|
||||
void writeLog(const char *format, ...) {
|
||||
if (!_logFile) return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(_logFile, format, args);
|
||||
fprintf(_logFile, "\n");
|
||||
fflush(_logFile);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void delFolder() {
|
||||
string delPath = "tupdates/ready", delFolder = "tupdates";
|
||||
writeLog("Fully clearing path '%s'..", delPath.c_str());
|
||||
if (!remove_directory(delPath)) {
|
||||
writeLog("Error: failed to clear path! :(");
|
||||
}
|
||||
rmdir(delFolder.c_str());
|
||||
}
|
||||
|
||||
bool update() {
|
||||
writeLog("Update started..");
|
||||
|
||||
string updDir = "tupdates/ready";
|
||||
|
||||
deque<string> dirs;
|
||||
dirs.push_back(updDir);
|
||||
|
||||
deque<string> from, to, forcedirs;
|
||||
|
||||
do {
|
||||
string dir = dirs.front();
|
||||
dirs.pop_front();
|
||||
|
||||
string toDir = exeDir;
|
||||
if (dir.size() > updDir.size() + 1) {
|
||||
toDir += (dir.substr(updDir.size() + 1) + '/');
|
||||
forcedirs.push_back(toDir);
|
||||
writeLog("Parsing dir '%s' in update tree..", toDir.c_str());
|
||||
}
|
||||
|
||||
DIR *d = opendir(dir.c_str());
|
||||
if (!d) {
|
||||
writeLog("Failed to open dir %s", dir.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
while (struct dirent *p = readdir(d)) {
|
||||
/* Skip the names "." and ".." as we don't want to recurse on them. */
|
||||
if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) continue;
|
||||
|
||||
string fname = dir + '/' + p->d_name;
|
||||
struct stat statbuf;
|
||||
if (!stat(fname.c_str(), &statbuf)) {
|
||||
if (S_ISDIR(statbuf.st_mode)) {
|
||||
dirs.push_back(fname);
|
||||
writeLog("Added dir '%s' in update tree..", fname.c_str());
|
||||
} else {
|
||||
string tofname = exeDir + fname.substr(updDir.size() + 1);
|
||||
if (equal(tofname, exeName)) { // bad update - has Updater - delete all dir
|
||||
writeLog("Error: bad update, has Updater! '%s' equal '%s'", tofname.c_str(), exeName.c_str());
|
||||
delFolder();
|
||||
return false;
|
||||
}
|
||||
from.push_back(fname);
|
||||
to.push_back(tofname);
|
||||
writeLog("Added file '%s' to be copied to '%s'", fname.c_str(), tofname.c_str());
|
||||
}
|
||||
} else {
|
||||
writeLog("Could not get stat() for file %s", fname.c_str());
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
} while (!dirs.empty());
|
||||
|
||||
for (size_t i = 0; i < forcedirs.size(); ++i) {
|
||||
string forcedir = forcedirs[i];
|
||||
writeLog("Forcing dir '%s'..", forcedir.c_str());
|
||||
if (!forcedir.empty() && !mkpath(forcedir.c_str())) {
|
||||
writeLog("Error: failed to create dir '%s'..", forcedir.c_str());
|
||||
delFolder();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < from.size(); ++i) {
|
||||
string fname = from[i], tofname = to[i];
|
||||
writeLog("Copying file '%s' to '%s'..", fname.c_str(), tofname.c_str());
|
||||
int copyTries = 0, triesLimit = 30;
|
||||
do {
|
||||
if (!copyFile(fname.c_str(), tofname.c_str())) {
|
||||
++copyTries;
|
||||
usleep(100000);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (copyTries < triesLimit);
|
||||
if (copyTries == triesLimit) {
|
||||
writeLog("Error: failed to copy, asking to retry..");
|
||||
delFolder();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
writeLog("Update succeed! Clearing folder..");
|
||||
delFolder();
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
openLog();
|
||||
|
||||
writeLog("Updater started..");
|
||||
|
||||
bool needupdate = false, autostart = false, debug = false, tosettings = false;
|
||||
char *key = 0;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (equal(argv[i], "-update")) {
|
||||
needupdate = true;
|
||||
} else if (equal(argv[i], "-autostart")) {
|
||||
autostart = true;
|
||||
} else if (equal(argv[i], "-debug")) {
|
||||
debug = _debug = true;
|
||||
openLog();
|
||||
} else if (equal(argv[i], "-tosettings")) {
|
||||
tosettings = true;
|
||||
} else if (equal(argv[i], "-key") && ++i < argc) {
|
||||
key = argv[i];
|
||||
}
|
||||
}
|
||||
if (needupdate) writeLog("Need to update!");
|
||||
if (autostart) writeLog("From autostart!");
|
||||
|
||||
exeName = argv[0];
|
||||
writeLog("Exe name is: %s", exeName.c_str());
|
||||
if (exeName.size() >= 7) {
|
||||
if (equal(exeName.substr(exeName.size() - 7), "Updater")) {
|
||||
exeDir = exeName.substr(0, exeName.size() - 7);
|
||||
writeLog("Exe dir is: %s", exeDir.c_str());
|
||||
if (needupdate) {
|
||||
update();
|
||||
}
|
||||
} else {
|
||||
writeLog("Error: bad exe name!");
|
||||
}
|
||||
} else {
|
||||
writeLog("Error: short exe name!");
|
||||
}
|
||||
|
||||
static const int MaxArgsCount = 128;
|
||||
char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key";
|
||||
int argIndex = 0;
|
||||
args[argIndex++] = p_noupdate;
|
||||
if (autostart) args[argIndex++] = p_autostart;
|
||||
if (debug) args[argIndex++] = p_debug;
|
||||
if (tosettings) args[argIndex++] = p_tosettings;
|
||||
if (key) {
|
||||
args[argIndex++] = p_key;
|
||||
args[argIndex++] = key;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
switch (pid) {
|
||||
case -1: writeLog("fork() failed!"); return 1;
|
||||
case 0: execv((exeDir + "Telegram").c_str(), args); return 1;
|
||||
}
|
||||
|
||||
writeLog("Executed Telegram, closing log and quiting..");
|
||||
closeLog();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -23,7 +23,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
|||
#include "application.h"
|
||||
#include "fileuploader.h"
|
||||
#include "mainwidget.h"
|
||||
#include <QtMultimedia/QSoundEffect>
|
||||
//#include <QtMultimedia/QSoundEffect>
|
||||
#include <libexif/exif-data.h>
|
||||
|
||||
namespace {
|
||||
|
@ -61,7 +61,7 @@ namespace {
|
|||
|
||||
HistoryItem *hoveredItem = 0, *pressedItem = 0, *hoveredLinkItem = 0, *pressedLinkItem = 0, *contextItem = 0, *mousedItem = 0;
|
||||
|
||||
QSoundEffect *newMsgSound = 0;
|
||||
// QSoundEffect *newMsgSound = 0;
|
||||
QPixmap *sprite = 0, *emojis = 0;
|
||||
|
||||
typedef QMap<uint32, QPixmap> EmojisMap;
|
||||
|
@ -321,7 +321,7 @@ namespace App {
|
|||
case mtpc_userStatusOnline: data->onlineTill = status->c_userStatusOnline().vexpires.v; break;
|
||||
}
|
||||
|
||||
if (data->contact < 0 && !data->phone.isEmpty() && (data->id & 0xFFFFFFFF) != MTP::authedId()) {
|
||||
if (data->contact < 0 && !data->phone.isEmpty() && int32(data->id & 0xFFFFFFFF) != MTP::authedId()) {
|
||||
data->contact = 0;
|
||||
}
|
||||
if (data->contact > 0 && !wasContact) {
|
||||
|
@ -1222,11 +1222,11 @@ namespace App {
|
|||
|
||||
void initMedia() {
|
||||
deinitMedia(false);
|
||||
if (!newMsgSound) {
|
||||
newMsgSound = new QSoundEffect();
|
||||
newMsgSound->setSource(QUrl::fromLocalFile(st::newMsgSound));
|
||||
newMsgSound->setVolume(1);
|
||||
}
|
||||
// if (!newMsgSound) {
|
||||
// newMsgSound = new QSoundEffect();
|
||||
// newMsgSound->setSource(QUrl::fromLocalFile(st::newMsgSound));
|
||||
// newMsgSound->setVolume(1);
|
||||
// }
|
||||
|
||||
if (!::sprite) {
|
||||
::sprite = new QPixmap(st::spriteFile);
|
||||
|
@ -1251,9 +1251,9 @@ namespace App {
|
|||
|
||||
if (completely) {
|
||||
LOG(("Deleting sound.."));
|
||||
delete newMsgSound;
|
||||
// delete newMsgSound;
|
||||
LOG(("Sound deleted!"));
|
||||
newMsgSound = 0;
|
||||
// newMsgSound = 0;
|
||||
|
||||
delete ::sprite;
|
||||
::sprite = 0;
|
||||
|
@ -1344,7 +1344,7 @@ namespace App {
|
|||
}
|
||||
|
||||
void playSound() {
|
||||
if (cSoundNotify() && newMsgSound) newMsgSound->play();
|
||||
// if (cSoundNotify() && newMsgSound) newMsgSound->play();
|
||||
}
|
||||
|
||||
void writeConfig() {
|
||||
|
|
|
@ -117,7 +117,7 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv),
|
|||
|
||||
window = new Window();
|
||||
|
||||
psInstallEventFilter();
|
||||
psInstallEventFilter();
|
||||
|
||||
updateCheckTimer.setSingleShot(true);
|
||||
|
||||
|
@ -143,7 +143,8 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv),
|
|||
}
|
||||
|
||||
void Application::onAppUpdate(const MTPhelp_AppUpdate &response) {
|
||||
updateRequestId = 0;
|
||||
updateRequestId = 0;
|
||||
|
||||
cSetLastUpdateCheck(unixtime());
|
||||
App::writeConfig();
|
||||
if (response.type() == mtpc_help_noAppUpdate) {
|
||||
|
|
|
@ -84,14 +84,14 @@ void AboutBox::paintEvent(QPaintEvent *e) {
|
|||
|
||||
p.setPen(st::black->p);
|
||||
p.setFont(st::aboutHeaderFont->f);
|
||||
p.drawText((_width - (_headerWidth + _subheaderWidth)) / 2, st::aboutHeaderTop + st::aboutHeaderFont->ascent, qsl("Telegram"));
|
||||
p.drawText((_width - (_headerWidth + _subheaderWidth)) / 2, st::aboutHeaderTop + st::aboutHeaderFont->ascent, qsl("Telegram"));
|
||||
|
||||
p.setFont(st::aboutSubheaderFont->f);
|
||||
p.drawText((_width - (_headerWidth + _subheaderWidth)) / 2 + _headerWidth, st::aboutHeaderTop + st::aboutSubheaderFont->ascent, qsl("Desktop"));
|
||||
p.drawText((_width - (_headerWidth + _subheaderWidth)) / 2 + _headerWidth, st::aboutHeaderTop + st::aboutSubheaderFont->ascent, qsl("Desktop"));
|
||||
|
||||
p.setFont(st::aboutVersionFont->f);
|
||||
p.setPen(st::aboutVersionColor->p);
|
||||
p.drawText((_width - _versionWidth) / 2, st::aboutVersionTop + st::aboutVersionFont->ascent, _versionText);
|
||||
p.drawText((_width - _versionWidth) / 2, st::aboutVersionTop + st::aboutVersionFont->ascent, _versionText);
|
||||
}
|
||||
} else {
|
||||
p.setOpacity(a_opacity.current());
|
||||
|
|
|
@ -98,7 +98,7 @@ void EmojiBox::fillBlocks() {
|
|||
for (uint32 i = 0; i < replacesCount; ++i) {
|
||||
Block block(getEmoji(replaces[i].code), QString::fromUtf8(replaces[i].replace));
|
||||
currentRow.push_back(block);
|
||||
if (currentRow.size() == replacesInRow) {
|
||||
if (uint32(currentRow.size()) == replacesInRow) {
|
||||
_blocks.push_back(currentRow);
|
||||
currentRow.resize(0);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace style {
|
|||
modified[_flags] = Font(this);
|
||||
|
||||
f.setPixelSize(size);
|
||||
f.setBold(_flags & FontBold);
|
||||
f.setBold(_flags & FontBold);
|
||||
f.setItalic(_flags & FontItalic);
|
||||
f.setUnderline(_flags & FontUnderline);
|
||||
f.setStyleStrategy(QFont::PreferQuality);
|
||||
|
|
|
@ -1245,9 +1245,9 @@ public:
|
|||
break;
|
||||
}
|
||||
}/**/
|
||||
for (; _lineEnd > _lineStart + 1; --_lineEnd) {
|
||||
for (; _lineEnd > _lineStart; --_lineEnd) {
|
||||
QChar ch = _t->_text.at(_lineEnd - 1);
|
||||
if (ch != QChar::Space && ch != QChar::LineFeed) {
|
||||
if ((ch != QChar::Space || _lineEnd == _lineStart + 1) && ch != QChar::LineFeed) {
|
||||
break;
|
||||
}
|
||||
}/**/
|
||||
|
|
|
@ -16,6 +16,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|||
Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include <iostream>
|
||||
#include "pspecific.h"
|
||||
|
||||
namespace {
|
||||
|
@ -71,6 +72,8 @@ void debugLogWrite(const char *file, int32 line, const QString &v) {
|
|||
OutputDebugString(reinterpret_cast<const wchar_t *>(msg.utf16()));
|
||||
#elif defined Q_OS_MAC
|
||||
objc_outputDebugString(msg);
|
||||
#elif defined Q_OS_LINUX && defined _DEBUG
|
||||
std::cout << msg.toUtf8().constData();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ namespace {
|
|||
|
||||
bool onErrorDefault(mtpRequestId requestId, const RPCError &error) {
|
||||
const QString &err(error.type());
|
||||
QRegularExpressionMatch m;;
|
||||
QRegularExpressionMatch m;
|
||||
if ((m = QRegularExpression("^(FILE|PHONE|NETWORK|USER)_MIGRATE_(\\d+)$").match(err)).hasMatch()) {
|
||||
if (!requestId) return false;
|
||||
|
||||
|
|
|
@ -404,7 +404,7 @@ namespace {
|
|||
LOG(("TCP Error: bad packet size %1").arg(size * sizeof(mtpPrime)));
|
||||
return mtpBuffer(1, -500);
|
||||
}
|
||||
if (packet[0] != size * sizeof(mtpPrime)) {
|
||||
if (packet[0] != int32(size * sizeof(mtpPrime))) {
|
||||
LOG(("TCP Error: bad packet header"));
|
||||
TCP_LOG(("TCP Error: bad packet header, packet: %1").arg(mb(packet, size * sizeof(mtpPrime)).str()));
|
||||
return mtpBuffer(1, -500);
|
||||
|
@ -2756,12 +2756,12 @@ void MTProtoConnectionPrivate::authKeyCreated() {
|
|||
|
||||
void MTProtoConnectionPrivate::clearAuthKeyData() {
|
||||
if (authKeyData) {
|
||||
#ifdef Q_OS_WIN
|
||||
SecureZeroMemory(authKeyData, sizeof(AuthKeyCreateData));
|
||||
#ifdef Q_OS_WIN // TODO
|
||||
// SecureZeroMemory(authKeyData, sizeof(AuthKeyCreateData));
|
||||
#else
|
||||
memset(authKeyData, 0, sizeof(AuthKeyCreateData));
|
||||
// memset(authKeyData, 0, sizeof(AuthKeyCreateData));
|
||||
#endif
|
||||
delete authKeyData;
|
||||
delete authKeyData;
|
||||
authKeyData = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
|||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
|
||||
#include "pspecific_linux.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
|
|
@ -0,0 +1,899 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
an unofficial desktop messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "pspecific.h"
|
||||
|
||||
#include "lang.h"
|
||||
#include "application.h"
|
||||
#include "mainwidget.h"
|
||||
|
||||
namespace {
|
||||
bool frameless = true;
|
||||
bool finished = true;
|
||||
|
||||
class _PsEventFilter : public QAbstractNativeEventFilter {
|
||||
public:
|
||||
_PsEventFilter() {
|
||||
}
|
||||
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) {
|
||||
Window *wnd = Application::wnd();
|
||||
if (!wnd) return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
_PsEventFilter *_psEventFilter = 0;
|
||||
|
||||
};
|
||||
|
||||
PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/iconround256.png")) {
|
||||
connect(&psIdleTimer, SIGNAL(timeout()), this, SLOT(psIdleTimeout()));
|
||||
psIdleTimer.setSingleShot(false);
|
||||
}
|
||||
|
||||
void PsMainWindow::psNotIdle() const {
|
||||
psIdleTimer.stop();
|
||||
if (psIdle) {
|
||||
psIdle = false;
|
||||
if (App::main()) App::main()->setOnline();
|
||||
if (App::wnd()) App::wnd()->checkHistoryActivation();
|
||||
}
|
||||
}
|
||||
|
||||
void PsMainWindow::psIdleTimeout() {
|
||||
int64 idleTime = 0;//objc_idleTime();
|
||||
if (idleTime >= 0) {
|
||||
if (idleTime <= IdleMsecs) {
|
||||
psNotIdle();
|
||||
}
|
||||
} else { // error
|
||||
psNotIdle();
|
||||
}
|
||||
}
|
||||
|
||||
bool PsMainWindow::psIsOnline(int state) const {
|
||||
if (state < 0) state = this->windowState();
|
||||
if (state & Qt::WindowMinimized) {
|
||||
return false;
|
||||
} else if (!isVisible()) {
|
||||
return false;
|
||||
}
|
||||
int64 idleTime = 0;//objc_idleTime();
|
||||
LOG(("App Info: idle time %1").arg(idleTime));
|
||||
if (idleTime >= 0) {
|
||||
if (idleTime > IdleMsecs) {
|
||||
if (!psIdle) {
|
||||
psIdle = true;
|
||||
psIdleTimer.start(900);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
psNotIdle();
|
||||
}
|
||||
} else { // error
|
||||
psNotIdle();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PsMainWindow::psIsActive(int state) const {
|
||||
if (state < 0) state = this->windowState();
|
||||
return isActiveWindow() && isVisible() && !(state & Qt::WindowMinimized) && !psIdle;
|
||||
}
|
||||
|
||||
void PsMainWindow::psRefreshTaskbarIcon() {
|
||||
}
|
||||
|
||||
void PsMainWindow::psUpdateWorkmode() {
|
||||
}
|
||||
|
||||
void PsMainWindow::psUpdateCounter() {
|
||||
int32 counter = App::histories().unreadFull;
|
||||
|
||||
setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram"));
|
||||
|
||||
QString cnt = (counter < 1000) ? QString("%1").arg(counter) : QString("..%1").arg(counter % 100, 2, 10, QChar('0'));
|
||||
//_private.setWindowBadge(counter ? cnt : QString());
|
||||
}
|
||||
|
||||
void PsMainWindow::psInitSize() {
|
||||
setMinimumWidth(st::wndMinWidth);
|
||||
setMinimumHeight(st::wndMinHeight);
|
||||
|
||||
TWindowPos pos(cWindowPos());
|
||||
QRect avail(QDesktopWidget().availableGeometry());
|
||||
bool maximized = false;
|
||||
QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight);
|
||||
if (pos.w && pos.h) {
|
||||
QList<QScreen*> screens = App::app()->screens();
|
||||
for (QList<QScreen*>::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) {
|
||||
QByteArray name = (*i)->name().toUtf8();
|
||||
if (pos.moncrc == hashCrc32(name.constData(), name.size())) {
|
||||
QRect screen((*i)->geometry());
|
||||
int32 w = screen.width(), h = screen.height();
|
||||
if (w >= st::wndMinWidth && h >= st::wndMinHeight) {
|
||||
if (pos.w > w) pos.w = w;
|
||||
if (pos.h > h) pos.h = h;
|
||||
pos.x += screen.x();
|
||||
pos.y += screen.y();
|
||||
if (pos.x < screen.x() + screen.width() - 10 && pos.y < screen.y() + screen.height() - 10) {
|
||||
geom = QRect(pos.x, pos.y, pos.w, pos.h);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos.y < 0) pos.y = 0;
|
||||
maximized = pos.maximized;
|
||||
}
|
||||
setGeometry(geom);
|
||||
}
|
||||
|
||||
void PsMainWindow::psInitFrameless() {
|
||||
psUpdatedPositionTimer.setSingleShot(true);
|
||||
connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition()));
|
||||
|
||||
if (frameless) {
|
||||
//setWindowFlags(Qt::FramelessWindowHint);
|
||||
}
|
||||
|
||||
connect(windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(psStateChanged(Qt::WindowState)));
|
||||
}
|
||||
|
||||
void PsMainWindow::psSavePosition(Qt::WindowState state) {
|
||||
if (state == Qt::WindowActive) state = windowHandle()->windowState();
|
||||
if (state == Qt::WindowMinimized || !posInited) return;
|
||||
|
||||
TWindowPos pos(cWindowPos()), curPos = pos;
|
||||
|
||||
if (state == Qt::WindowMaximized) {
|
||||
curPos.maximized = 1;
|
||||
} else {
|
||||
QRect r(geometry());
|
||||
curPos.x = r.x();
|
||||
curPos.y = r.y();
|
||||
curPos.w = r.width();
|
||||
curPos.h = r.height();
|
||||
curPos.maximized = 0;
|
||||
}
|
||||
|
||||
int px = curPos.x + curPos.w / 2, py = curPos.y + curPos.h / 2, d = 0;
|
||||
QScreen *chosen = 0;
|
||||
QList<QScreen*> screens = App::app()->screens();
|
||||
for (QList<QScreen*>::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) {
|
||||
int dx = (*i)->geometry().x() + (*i)->geometry().width() / 2 - px; if (dx < 0) dx = -dx;
|
||||
int dy = (*i)->geometry().y() + (*i)->geometry().height() / 2 - py; if (dy < 0) dy = -dy;
|
||||
if (!chosen || dx + dy < d) {
|
||||
d = dx + dy;
|
||||
chosen = *i;
|
||||
}
|
||||
}
|
||||
if (chosen) {
|
||||
curPos.x -= chosen->geometry().x();
|
||||
curPos.y -= chosen->geometry().y();
|
||||
QByteArray name = chosen->name().toUtf8();
|
||||
curPos.moncrc = hashCrc32(name.constData(), name.size());
|
||||
}
|
||||
|
||||
if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) {
|
||||
if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) {
|
||||
cSetWindowPos(curPos);
|
||||
App::writeConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PsMainWindow::psUpdatedPosition() {
|
||||
psUpdatedPositionTimer.start(4000);
|
||||
}
|
||||
|
||||
void PsMainWindow::psStateChanged(Qt::WindowState state) {
|
||||
psUpdateSysMenu(state);
|
||||
psUpdateMargins();
|
||||
// if (state == Qt::WindowMinimized && GetWindowLong(ps_hWnd, GWL_HWNDPARENT)) {
|
||||
// minimizeToTray();
|
||||
// }
|
||||
psSavePosition(state);
|
||||
}
|
||||
|
||||
void PsMainWindow::psFirstShow() {
|
||||
finished = false;
|
||||
|
||||
psUpdateMargins();
|
||||
|
||||
bool showShadows = true;
|
||||
|
||||
show();
|
||||
//_private.enableShadow(winId());
|
||||
if (cWindowPos().maximized) {
|
||||
setWindowState(Qt::WindowMaximized);
|
||||
}
|
||||
|
||||
if (cFromAutoStart()) {
|
||||
if (cStartMinimized()) {
|
||||
setWindowState(Qt::WindowMinimized);
|
||||
if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) {
|
||||
hide();
|
||||
} else {
|
||||
show();
|
||||
}
|
||||
showShadows = false;
|
||||
} else {
|
||||
show();
|
||||
}
|
||||
} else {
|
||||
show();
|
||||
}
|
||||
posInited = true;
|
||||
}
|
||||
|
||||
bool PsMainWindow::psHandleTitle() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void PsMainWindow::psInitSysMenu() {
|
||||
}
|
||||
|
||||
void PsMainWindow::psUpdateSysMenu(Qt::WindowState state) {
|
||||
}
|
||||
|
||||
void PsMainWindow::psUpdateMargins() {
|
||||
}
|
||||
|
||||
void PsMainWindow::psFlash() {
|
||||
//_private.startBounce();
|
||||
}
|
||||
|
||||
PsMainWindow::~PsMainWindow() {
|
||||
finished = true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
QRect _monitorRect;
|
||||
uint64 _monitorLastGot = 0;
|
||||
}
|
||||
|
||||
QRect psDesktopRect() {
|
||||
uint64 tnow = getms();
|
||||
if (tnow > _monitorLastGot + 1000 || tnow < _monitorLastGot) {
|
||||
_monitorLastGot = tnow;
|
||||
_monitorRect = QApplication::desktop()->availableGeometry(App::wnd());
|
||||
}
|
||||
return _monitorRect;
|
||||
}
|
||||
|
||||
void PsMainWindow::psActivateNotify(NotifyWindow *w) {
|
||||
}
|
||||
|
||||
void PsMainWindow::psClearNotifies(PeerId peerId) {
|
||||
}
|
||||
|
||||
void PsMainWindow::psNotifyShown(NotifyWindow *w) {
|
||||
}
|
||||
|
||||
void PsMainWindow::psPlatformNotify(HistoryItem *item) {
|
||||
}
|
||||
|
||||
PsApplication::PsApplication(int &argc, char **argv) : QApplication(argc, argv) {
|
||||
}
|
||||
|
||||
void PsApplication::psInstallEventFilter() {
|
||||
delete _psEventFilter;
|
||||
_psEventFilter = new _PsEventFilter();
|
||||
installNativeEventFilter(_psEventFilter);
|
||||
}
|
||||
|
||||
PsApplication::~PsApplication() {
|
||||
delete _psEventFilter;
|
||||
_psEventFilter = 0;
|
||||
}
|
||||
|
||||
PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update) : reply(0), already(0), full(0) {
|
||||
updateUrl = qs(update.vurl);
|
||||
moveToThread(thread);
|
||||
manager.moveToThread(thread);
|
||||
App::setProxySettings(manager);
|
||||
|
||||
connect(thread, SIGNAL(started()), this, SLOT(start()));
|
||||
initOutput();
|
||||
}
|
||||
|
||||
PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const QString &url) : reply(0), already(0), full(0) {
|
||||
updateUrl = url;
|
||||
moveToThread(thread);
|
||||
manager.moveToThread(thread);
|
||||
App::setProxySettings(manager);
|
||||
|
||||
connect(thread, SIGNAL(started()), this, SLOT(start()));
|
||||
initOutput();
|
||||
}
|
||||
|
||||
void PsUpdateDownloader::initOutput() {
|
||||
QString fileName;
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("/([^/\\?]+)(\\?|$)")).match(updateUrl);
|
||||
if (m.hasMatch()) {
|
||||
fileName = m.captured(1).replace(QRegularExpression(qsl("[^a-zA-Z0-9_\\-]")), QString());
|
||||
}
|
||||
if (fileName.isEmpty()) {
|
||||
fileName = qsl("tupdate-%1").arg(rand());
|
||||
}
|
||||
QString dirStr = cWorkingDir() + qsl("tupdates/");
|
||||
fileName = dirStr + fileName;
|
||||
QFileInfo file(fileName);
|
||||
|
||||
QDir dir(dirStr);
|
||||
if (dir.exists()) {
|
||||
QFileInfoList all = dir.entryInfoList(QDir::Files);
|
||||
for (QFileInfoList::iterator i = all.begin(), e = all.end(); i != e; ++i) {
|
||||
if (i->absoluteFilePath() != file.absoluteFilePath()) {
|
||||
QFile::remove(i->absoluteFilePath());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dir.mkdir(dir.absolutePath());
|
||||
}
|
||||
outputFile.setFileName(fileName);
|
||||
if (file.exists()) {
|
||||
uint64 fullSize = file.size();
|
||||
if (fullSize < INT_MAX) {
|
||||
int32 goodSize = (int32)fullSize;
|
||||
if (goodSize % UpdateChunk) {
|
||||
goodSize = goodSize - (goodSize % UpdateChunk);
|
||||
if (goodSize) {
|
||||
if (outputFile.open(QIODevice::ReadOnly)) {
|
||||
QByteArray goodData = outputFile.readAll().mid(0, goodSize);
|
||||
outputFile.close();
|
||||
if (outputFile.open(QIODevice::WriteOnly)) {
|
||||
outputFile.write(goodData);
|
||||
outputFile.close();
|
||||
|
||||
QMutexLocker lock(&mutex);
|
||||
already = goodSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QMutexLocker lock(&mutex);
|
||||
already = goodSize;
|
||||
}
|
||||
}
|
||||
if (!already) {
|
||||
QFile::remove(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PsUpdateDownloader::start() {
|
||||
sendRequest();
|
||||
}
|
||||
|
||||
void PsUpdateDownloader::sendRequest() {
|
||||
QNetworkRequest req(updateUrl);
|
||||
QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";// + QByteArray::number(already + cUpdateChunk() - 1);
|
||||
req.setRawHeader("Range", rangeHeaderValue);
|
||||
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
|
||||
if (reply) reply->deleteLater();
|
||||
reply = manager.get(req);
|
||||
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(partFinished(qint64,qint64)));
|
||||
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(partFailed(QNetworkReply::NetworkError)));
|
||||
connect(reply, SIGNAL(metaDataChanged()), this, SLOT(partMetaGot()));
|
||||
}
|
||||
|
||||
void PsUpdateDownloader::partMetaGot() {
|
||||
typedef QList<QNetworkReply::RawHeaderPair> Pairs;
|
||||
Pairs pairs = reply->rawHeaderPairs();
|
||||
for (Pairs::iterator i = pairs.begin(), e = pairs.end(); i != e; ++i) {
|
||||
if (QString::fromUtf8(i->first).toLower() == "content-range") {
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("/(\\d+)([^\\d]|$)")).match(QString::fromUtf8(i->second));
|
||||
if (m.hasMatch()) {
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
full = m.captured(1).toInt();
|
||||
}
|
||||
emit App::app()->updateDownloading(already, full);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 PsUpdateDownloader::ready() {
|
||||
QMutexLocker lock(&mutex);
|
||||
return already;
|
||||
}
|
||||
|
||||
int32 PsUpdateDownloader::size() {
|
||||
QMutexLocker lock(&mutex);
|
||||
return full;
|
||||
}
|
||||
|
||||
void PsUpdateDownloader::partFinished(qint64 got, qint64 total) {
|
||||
if (!reply) return;
|
||||
|
||||
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
if (statusCode.isValid()) {
|
||||
int status = statusCode.toInt();
|
||||
if (status != 200 && status != 206 && status != 416) {
|
||||
LOG(("Update Error: Bad HTTP status received in partFinished(): %1").arg(status));
|
||||
return fatalFail();
|
||||
}
|
||||
}
|
||||
|
||||
if (!already && !full) {
|
||||
QMutexLocker lock(&mutex);
|
||||
full = total;
|
||||
}
|
||||
DEBUG_LOG(("Update Info: part %1 of %2").arg(got).arg(total));
|
||||
|
||||
if (!outputFile.isOpen()) {
|
||||
if (!outputFile.open(QIODevice::Append)) {
|
||||
LOG(("Update Error: Could not open output file '%1' for appending").arg(outputFile.fileName()));
|
||||
return fatalFail();
|
||||
}
|
||||
}
|
||||
QByteArray r = reply->readAll();
|
||||
if (!r.isEmpty()) {
|
||||
outputFile.write(r);
|
||||
|
||||
QMutexLocker lock(&mutex);
|
||||
already += r.size();
|
||||
}
|
||||
if (got >= total) {
|
||||
reply->deleteLater();
|
||||
reply = 0;
|
||||
outputFile.close();
|
||||
unpackUpdate();
|
||||
} else {
|
||||
emit App::app()->updateDownloading(already, full);
|
||||
}
|
||||
}
|
||||
|
||||
void PsUpdateDownloader::partFailed(QNetworkReply::NetworkError e) {
|
||||
if (!reply) return;
|
||||
|
||||
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
reply->deleteLater();
|
||||
reply = 0;
|
||||
if (statusCode.isValid()) {
|
||||
int status = statusCode.toInt();
|
||||
if (status == 416) { // Requested range not satisfiable
|
||||
outputFile.close();
|
||||
unpackUpdate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG(("Update Error: failed to download part starting from %1, error %2").arg(already).arg(e));
|
||||
emit App::app()->updateFailed();
|
||||
}
|
||||
|
||||
void PsUpdateDownloader::deleteDir(const QString &dir) {
|
||||
// objc_deleteDir(dir);
|
||||
}
|
||||
|
||||
void PsUpdateDownloader::fatalFail() {
|
||||
clearAll();
|
||||
emit App::app()->updateFailed();
|
||||
}
|
||||
|
||||
void PsUpdateDownloader::clearAll() {
|
||||
deleteDir(cWorkingDir() + qsl("tupdates"));
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
typedef DWORD VerInt;
|
||||
typedef WCHAR VerChar;
|
||||
#else
|
||||
typedef int VerInt;
|
||||
typedef wchar_t VerChar;
|
||||
#endif
|
||||
|
||||
void PsUpdateDownloader::unpackUpdate() {
|
||||
QByteArray packed;
|
||||
if (!outputFile.open(QIODevice::ReadOnly)) {
|
||||
LOG(("Update Error: cant read updates file!"));
|
||||
return fatalFail();
|
||||
}
|
||||
#ifdef Q_OS_WIN // use Lzma SDK for win
|
||||
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
|
||||
#else
|
||||
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = 0, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hOriginalSizeLen; // header
|
||||
#endif
|
||||
QByteArray compressed = outputFile.readAll();
|
||||
int32 compressedLen = compressed.size() - hSize;
|
||||
if (compressedLen <= 0) {
|
||||
LOG(("Update Error: bad compressed size: %1").arg(compressed.size()));
|
||||
return fatalFail();
|
||||
}
|
||||
outputFile.close();
|
||||
|
||||
QString tempDirPath = cWorkingDir() + qsl("tupdates/temp"), readyDirPath = cWorkingDir() + qsl("tupdates/ready");
|
||||
deleteDir(tempDirPath);
|
||||
deleteDir(readyDirPath);
|
||||
|
||||
QDir tempDir(tempDirPath), readyDir(readyDirPath);
|
||||
if (tempDir.exists() || readyDir.exists()) {
|
||||
LOG(("Update Error: cant clear tupdates/temp or tupdates/ready dir!"));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
uchar sha1Buffer[20];
|
||||
bool goodSha1 = !memcmp(compressed.constData() + hSigLen, hashSha1(compressed.constData() + hSigLen + hShaLen, compressedLen + hPropsLen + hOriginalSizeLen, sha1Buffer), hShaLen);
|
||||
if (!goodSha1) {
|
||||
LOG(("Update Error: bad SHA1 hash of update file!"));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
RSA *pbKey = PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<char*>(UpdatesPublicKey), -1), 0, 0, 0);
|
||||
if (!pbKey) {
|
||||
LOG(("Update Error: cant read public rsa key!"));
|
||||
return fatalFail();
|
||||
}
|
||||
if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), hSigLen, pbKey) != 1) { // verify signature
|
||||
RSA_free(pbKey);
|
||||
LOG(("Update Error: bad RSA signature of update file!"));
|
||||
return fatalFail();
|
||||
}
|
||||
RSA_free(pbKey);
|
||||
|
||||
QByteArray uncompressed;
|
||||
|
||||
int32 uncompressedLen;
|
||||
memcpy(&uncompressedLen, compressed.constData() + hSigLen + hShaLen + hPropsLen, hOriginalSizeLen);
|
||||
uncompressed.resize(uncompressedLen);
|
||||
|
||||
size_t resultLen = uncompressed.size();
|
||||
#ifdef Q_OS_WIN // use Lzma SDK for win
|
||||
SizeT srcLen = compressedLen;
|
||||
int uncompressRes = LzmaUncompress((uchar*)uncompressed.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE);
|
||||
if (uncompressRes != SZ_OK) {
|
||||
LOG(("Update Error: could not uncompress lzma, code: %1").arg(uncompressRes));
|
||||
return fatalFail();
|
||||
}
|
||||
#else
|
||||
lzma_stream stream = LZMA_STREAM_INIT;
|
||||
|
||||
lzma_ret ret = lzma_stream_decoder(&stream, UINT64_MAX, LZMA_CONCATENATED);
|
||||
if (ret != LZMA_OK) {
|
||||
const char *msg;
|
||||
switch (ret) {
|
||||
case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
|
||||
case LZMA_OPTIONS_ERROR: msg = "Specified preset is not supported"; break;
|
||||
case LZMA_UNSUPPORTED_CHECK: msg = "Specified integrity check is not supported"; break;
|
||||
default: msg = "Unknown error, possibly a bug"; break;
|
||||
}
|
||||
LOG(("Error initializing the decoder: %1 (error code %2)").arg(msg).arg(ret));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
stream.avail_in = compressedLen;
|
||||
stream.next_in = (uint8_t*)(compressed.constData() + hSize);
|
||||
stream.avail_out = resultLen;
|
||||
stream.next_out = (uint8_t*)uncompressed.data();
|
||||
|
||||
lzma_ret res = lzma_code(&stream, LZMA_FINISH);
|
||||
if (stream.avail_in) {
|
||||
LOG(("Error in decompression, %1 bytes left in _in of %2 whole.").arg(stream.avail_in).arg(compressedLen));
|
||||
return fatalFail();
|
||||
} else if (stream.avail_out) {
|
||||
LOG(("Error in decompression, %1 bytes free left in _out of %2 whole.").arg(stream.avail_out).arg(resultLen));
|
||||
return fatalFail();
|
||||
}
|
||||
lzma_end(&stream);
|
||||
if (res != LZMA_OK && res != LZMA_STREAM_END) {
|
||||
const char *msg;
|
||||
switch (res) {
|
||||
case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
|
||||
case LZMA_FORMAT_ERROR: msg = "The input data is not in the .xz format"; break;
|
||||
case LZMA_OPTIONS_ERROR: msg = "Unsupported compression options"; break;
|
||||
case LZMA_DATA_ERROR: msg = "Compressed file is corrupt"; break;
|
||||
case LZMA_BUF_ERROR: msg = "Compressed data is truncated or otherwise corrupt"; break;
|
||||
default: msg = "Unknown error, possibly a bug"; break;
|
||||
}
|
||||
LOG(("Error in decompression: %1 (error code %2)").arg(msg).arg(res));
|
||||
return fatalFail();
|
||||
}
|
||||
#endif
|
||||
|
||||
tempDir.mkdir(tempDir.absolutePath());
|
||||
|
||||
quint32 version;
|
||||
{
|
||||
QBuffer buffer(&uncompressed);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QDataStream stream(&buffer);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
|
||||
stream >> version;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("Update Error: cant read version from downloaded stream, status: %1").arg(stream.status()));
|
||||
return fatalFail();
|
||||
}
|
||||
if (int32(version) <= AppVersion) {
|
||||
LOG(("Update Error: downloaded version %1 is not greater, than mine %2").arg(version).arg(AppVersion));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
quint32 filesCount;
|
||||
stream >> filesCount;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("Update Error: cant read files count from downloaded stream, status: %1").arg(stream.status()));
|
||||
return fatalFail();
|
||||
}
|
||||
if (!filesCount) {
|
||||
LOG(("Update Error: update is empty!"));
|
||||
return fatalFail();
|
||||
}
|
||||
for (uint32 i = 0; i < filesCount; ++i) {
|
||||
QString relativeName;
|
||||
quint32 fileSize;
|
||||
QByteArray fileInnerData;
|
||||
bool executable = false;
|
||||
|
||||
stream >> relativeName >> fileSize >> fileInnerData;
|
||||
#if defined Q_OS_MAC || defined Q_OS_LINUX
|
||||
stream >> executable;
|
||||
#endif
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("Update Error: cant read file from downloaded stream, status: %1").arg(stream.status()));
|
||||
return fatalFail();
|
||||
}
|
||||
if (fileSize != quint32(fileInnerData.size())) {
|
||||
LOG(("Update Error: bad file size %1 not matching data size %2").arg(fileSize).arg(fileInnerData.size()));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
QFile f(tempDirPath + '/' + relativeName);
|
||||
if (!QDir().mkpath(QFileInfo(f).absolutePath())) {
|
||||
LOG(("Update Error: cant mkpath for file '%1'").arg(tempDirPath + '/' + relativeName));
|
||||
return fatalFail();
|
||||
}
|
||||
if (!f.open(QIODevice::WriteOnly)) {
|
||||
LOG(("Update Error: cant open file '%1' for writing").arg(tempDirPath + '/' + relativeName));
|
||||
return fatalFail();
|
||||
}
|
||||
if (f.write(fileInnerData) != fileSize) {
|
||||
f.close();
|
||||
LOG(("Update Error: cant write file '%1'").arg(tempDirPath + '/' + relativeName));
|
||||
return fatalFail();
|
||||
}
|
||||
f.close();
|
||||
if (executable) {
|
||||
QFileDevice::Permissions p = f.permissions();
|
||||
p |= QFileDevice::ExeOwner | QFileDevice::ExeUser | QFileDevice::ExeGroup | QFileDevice::ExeOther;
|
||||
f.setPermissions(p);
|
||||
}
|
||||
}
|
||||
|
||||
// create tdata/version file
|
||||
tempDir.mkdir(QDir(tempDirPath + qsl("/tdata")).absolutePath());
|
||||
std::wstring versionString = ((version % 1000) ? QString("%1.%2.%3").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000)).arg(int(version % 1000)) : QString("%1.%2").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000))).toStdWString();
|
||||
|
||||
VerInt versionNum = VerInt(version), versionLen = VerInt(versionString.size() * sizeof(VerChar));
|
||||
VerChar versionStr[32];
|
||||
memcpy(versionStr, versionString.c_str(), versionLen);
|
||||
|
||||
QFile fVersion(tempDirPath + qsl("/tdata/version"));
|
||||
if (!fVersion.open(QIODevice::WriteOnly)) {
|
||||
LOG(("Update Error: cant write version file '%1'").arg(tempDirPath + qsl("/version")));
|
||||
return fatalFail();
|
||||
}
|
||||
fVersion.write((const char*)&versionNum, sizeof(VerInt));
|
||||
fVersion.write((const char*)&versionLen, sizeof(VerInt));
|
||||
fVersion.write((const char*)&versionStr[0], versionLen);
|
||||
fVersion.close();
|
||||
}
|
||||
|
||||
if (!tempDir.rename(tempDir.absolutePath(), readyDir.absolutePath())) {
|
||||
LOG(("Update Error: cant rename temp dir '%1' to ready dir '%2'").arg(tempDir.absolutePath()).arg(readyDir.absolutePath()));
|
||||
return fatalFail();
|
||||
}
|
||||
deleteDir(tempDirPath);
|
||||
outputFile.remove();
|
||||
|
||||
emit App::app()->updateReady();
|
||||
}
|
||||
|
||||
PsUpdateDownloader::~PsUpdateDownloader() {
|
||||
delete reply;
|
||||
reply = 0;
|
||||
}
|
||||
|
||||
void psActivateProcess(uint64 pid) {
|
||||
// objc_activateProgram();
|
||||
}
|
||||
|
||||
QString psCurrentCountry() {
|
||||
QString country;// = objc_currentCountry();
|
||||
return country.isEmpty() ? QString::fromLatin1(DefaultCountry) : country;
|
||||
}
|
||||
|
||||
QString psCurrentLanguage() {
|
||||
QString lng;// = objc_currentLang();
|
||||
return lng.isEmpty() ? QString::fromLatin1(DefaultLanguage) : lng;
|
||||
}
|
||||
|
||||
QString psAppDataPath() {
|
||||
return QString();//objc_appDataPath();
|
||||
}
|
||||
|
||||
QString psCurrentExeDirectory(int argc, char *argv[]) {
|
||||
QString first = argc ? QString::fromLocal8Bit(argv[0]) : QString();
|
||||
if (!first.isEmpty()) {
|
||||
QFileInfo info(first);
|
||||
if (info.exists()) {
|
||||
QDir result(info.absolutePath());
|
||||
return result.absolutePath() + '/';
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void psDoCleanup() {
|
||||
try {
|
||||
psAutoStart(false, true);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
int psCleanup() {
|
||||
psDoCleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void psDoFixPrevious() {
|
||||
}
|
||||
|
||||
int psFixPrevious() {
|
||||
psDoFixPrevious();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool psCheckReadyUpdate() {
|
||||
QString readyPath = cWorkingDir() + qsl("tupdates/ready");
|
||||
if (!QDir(readyPath).exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check ready version
|
||||
QString versionPath = readyPath + qsl("/tdata/version");
|
||||
{
|
||||
QFile fVersion(versionPath);
|
||||
if (!fVersion.open(QIODevice::ReadOnly)) {
|
||||
LOG(("Update Error: cant read version file '%1'").arg(versionPath));
|
||||
PsUpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
VerInt versionNum;
|
||||
if (fVersion.read((char*)&versionNum, sizeof(VerInt)) != sizeof(VerInt)) {
|
||||
LOG(("Update Error: cant read version from file '%1'").arg(versionPath));
|
||||
PsUpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
fVersion.close();
|
||||
if (versionNum <= AppVersion) {
|
||||
LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
|
||||
PsUpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QString curUpdater = (cExeDir() + "Updater.exe");
|
||||
QFileInfo updater(cWorkingDir() + "tupdates/ready/Updater.exe");
|
||||
#elif defined Q_OS_MAC
|
||||
QString curUpdater = (cExeDir() + "Telegram.app/Contents/Frameworks/Updater");
|
||||
QFileInfo updater(cWorkingDir() + "tupdates/ready/Telegram.app/Contents/Frameworks/Updater");
|
||||
#elif defined Q_OS_LINUX
|
||||
QString curUpdater;
|
||||
QFileInfo updater;
|
||||
#endif
|
||||
if (!updater.exists()) {
|
||||
QFileInfo current(curUpdater);
|
||||
if (!current.exists()) {
|
||||
PsUpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) {
|
||||
PsUpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
if (CopyFile(updater.absoluteFilePath().toStdWString().c_str(), curUpdater.toStdWString().c_str(), FALSE) == FALSE) {
|
||||
PsUpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
|
||||
PsUpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
#elif defined Q_OS_MAC
|
||||
QFileInfo to(curUpdater);
|
||||
QDir().mkpath(to.absolutePath());
|
||||
if (!objc_moveFile(updater.absoluteFilePath(), curUpdater)) {
|
||||
PsUpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void psPostprocessFile(const QString &name) {
|
||||
}
|
||||
|
||||
void psOpenFile(const QString &name, bool openWith) {
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(name));
|
||||
//objc_openFile(name, openWith);
|
||||
}
|
||||
|
||||
void psShowInFolder(const QString &name) {
|
||||
QDesktopServices::openUrl(QFileInfo(name).absoluteDir().absolutePath());
|
||||
// system(("nautilus " + QFileInfo(name).absoluteDir().absolutePath()).toUtf8().constData());
|
||||
//objc_showInFinder(name, QFileInfo(name).absolutePath());
|
||||
}
|
||||
|
||||
void psFinish() {
|
||||
//objc_finish();
|
||||
}
|
||||
|
||||
bool _execUpdater(bool update = true) {
|
||||
static const int MaxArgsCount = 128, MaxLen = 65536;
|
||||
char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key";
|
||||
char p_datafile[MaxLen] = {0};
|
||||
int argIndex = 0;
|
||||
if (!update) {
|
||||
args[argIndex++] = p_noupdate;
|
||||
args[argIndex++] = p_tosettings;
|
||||
}
|
||||
if (cFromAutoStart()) args[argIndex++] = p_autostart;
|
||||
if (cDebug()) args[argIndex++] = p_debug;
|
||||
if (cDataFile() != (cTestMode() ? qsl("data_test") : qsl("data"))) {
|
||||
QByteArray dataf = cDataFile().toUtf8();
|
||||
if (dataf.size() < MaxLen) {
|
||||
memcpy(p_datafile, dataf.constData(), dataf.size());
|
||||
args[argIndex++] = p_key;
|
||||
args[argIndex++] = p_datafile;
|
||||
}
|
||||
}
|
||||
char path[MaxLen] = {0};
|
||||
QByteArray data((cExeDir() + "Updater").toUtf8());
|
||||
memcpy(path, data.constData(), data.size());
|
||||
|
||||
pid_t pid = fork();
|
||||
switch (pid) {
|
||||
case -1: return false;
|
||||
case 0: execv(path, args); return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void psExecUpdater() {
|
||||
if (!_execUpdater()) {
|
||||
QString readyPath = cWorkingDir() + qsl("tupdates/ready");
|
||||
PsUpdateDownloader::deleteDir(readyPath);
|
||||
}
|
||||
}
|
||||
|
||||
void psExecTelegram() {
|
||||
_execUpdater(false);
|
||||
}
|
||||
|
||||
void psAutoStart(bool start, bool silent) {
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
an unofficial desktop messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
inline QString psServerPrefix() {
|
||||
return qsl("/tmp/");
|
||||
}
|
||||
inline void psCheckLocalSocket(const QString &serverName) {
|
||||
QFile address(serverName);
|
||||
if (address.exists()) {
|
||||
address.remove();
|
||||
}
|
||||
}
|
||||
|
||||
class NotifyWindow;
|
||||
|
||||
class PsMainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PsMainWindow(QWidget *parent = 0);
|
||||
|
||||
int32 psResizeRowWidth() const {
|
||||
return 0;//st::wndResizeAreaWidth;
|
||||
}
|
||||
|
||||
void psInitFrameless();
|
||||
void psInitSize();
|
||||
|
||||
void psFirstShow();
|
||||
void psInitSysMenu();
|
||||
void psUpdateSysMenu(Qt::WindowState state);
|
||||
void psUpdateMargins();
|
||||
void psUpdatedPosition();
|
||||
|
||||
bool psHandleTitle();
|
||||
|
||||
void psFlash();
|
||||
void psNotifySettingGot();
|
||||
|
||||
bool psIsActive(int state = -1) const;
|
||||
bool psIsOnline(int state) const;
|
||||
|
||||
void psUpdateWorkmode();
|
||||
|
||||
void psRefreshTaskbarIcon();
|
||||
virtual bool minimizeToTray() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool psPosInited() const {
|
||||
return posInited;
|
||||
}
|
||||
|
||||
void psActivateNotify(NotifyWindow *w);
|
||||
void psClearNotifies(PeerId peerId = 0);
|
||||
void psNotifyShown(NotifyWindow *w);
|
||||
void psPlatformNotify(HistoryItem *item);
|
||||
|
||||
~PsMainWindow();
|
||||
|
||||
public slots:
|
||||
|
||||
void psStateChanged(Qt::WindowState state);
|
||||
void psUpdateCounter();
|
||||
void psSavePosition(Qt::WindowState state = Qt::WindowActive);
|
||||
void psIdleTimeout();
|
||||
|
||||
protected:
|
||||
|
||||
void psNotIdle() const;
|
||||
|
||||
bool posInited;
|
||||
QSystemTrayIcon *trayIcon;
|
||||
QMenu *trayIconMenu;
|
||||
QImage icon256;
|
||||
virtual void setupTrayIcon() {
|
||||
}
|
||||
|
||||
QTimer psUpdatedPositionTimer;
|
||||
|
||||
private:
|
||||
mutable bool psIdle;
|
||||
mutable QTimer psIdleTimer;
|
||||
};
|
||||
|
||||
|
||||
class PsApplication : public QApplication {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
PsApplication(int &argc, char **argv);
|
||||
void psInstallEventFilter();
|
||||
~PsApplication();
|
||||
|
||||
signals:
|
||||
|
||||
void updateChecking();
|
||||
void updateLatest();
|
||||
void updateDownloading(qint64 ready, qint64 total);
|
||||
void updateReady();
|
||||
void updateFailed();
|
||||
|
||||
};
|
||||
|
||||
class PsUpdateDownloader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update);
|
||||
PsUpdateDownloader(QThread *thread, const QString &url);
|
||||
|
||||
void unpackUpdate();
|
||||
|
||||
int32 ready();
|
||||
int32 size();
|
||||
|
||||
static void deleteDir(const QString &dir);
|
||||
static void clearAll();
|
||||
|
||||
~PsUpdateDownloader();
|
||||
|
||||
public slots:
|
||||
|
||||
void start();
|
||||
void partMetaGot();
|
||||
void partFinished(qint64 got, qint64 total);
|
||||
void partFailed(QNetworkReply::NetworkError e);
|
||||
void sendRequest();
|
||||
|
||||
private:
|
||||
void initOutput();
|
||||
|
||||
void fatalFail();
|
||||
|
||||
QString updateUrl;
|
||||
QNetworkAccessManager manager;
|
||||
QNetworkReply *reply;
|
||||
int32 already, full;
|
||||
QFile outputFile;
|
||||
|
||||
QMutex mutex;
|
||||
|
||||
};
|
||||
|
||||
void psActivateProcess(uint64 pid);
|
||||
QString psLocalServerPrefix();
|
||||
QString psCurrentCountry();
|
||||
QString psCurrentLanguage();
|
||||
QString psAppDataPath();
|
||||
QString psCurrentExeDirectory(int argc, char *argv[]);
|
||||
void psAutoStart(bool start, bool silent = false);
|
||||
|
||||
QRect psDesktopRect();
|
||||
|
||||
int psCleanup();
|
||||
int psFixPrevious();
|
||||
|
||||
bool psCheckReadyUpdate();
|
||||
void psExecUpdater();
|
||||
void psExecTelegram();
|
||||
|
||||
void psPostprocessFile(const QString &name);
|
||||
void psOpenFile(const QString &name, bool openWith = false);
|
||||
void psShowInFolder(const QString &name);
|
||||
void psFinish();
|
|
@ -68,8 +68,11 @@ QString gLangFile;
|
|||
bool gRetina = false;
|
||||
float64 gRetinaFactor = 1.;
|
||||
int32 gIntRetinaFactor = 1;
|
||||
#ifdef Q_OS_MAC
|
||||
bool gCustomNotifies = false;
|
||||
|
||||
#else
|
||||
bool gCustomNotifies = true;
|
||||
#endif
|
||||
uint64 gInstance = 0.;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
|
|
@ -22,9 +22,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
|||
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
|
||||
Q_IMPORT_PLUGIN(QWindowsAudioPlugin)
|
||||
Q_IMPORT_PLUGIN(AccessibleFactory)
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#elif defined Q_OS_MAC
|
||||
//Q_IMPORT_PLUGIN(AVFServicePlugin)
|
||||
Q_IMPORT_PLUGIN(AVFMediaPlayerServicePlugin)
|
||||
Q_IMPORT_PLUGIN(QT7ServicePlugin)
|
||||
|
@ -44,4 +42,6 @@ Q_IMPORT_PLUGIN(QTgaPlugin)
|
|||
Q_IMPORT_PLUGIN(QTiffPlugin)
|
||||
Q_IMPORT_PLUGIN(QWbmpPlugin)
|
||||
Q_IMPORT_PLUGIN(QWebpPlugin)
|
||||
#elif defined Q_OS_LINUX
|
||||
Q_IMPORT_PLUGIN(QPulseAudioPlugin)
|
||||
#endif
|
||||
|
|
|
@ -77,7 +77,7 @@ TitleWidget::TitleWidget(Window *window)
|
|||
connect(wnd->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(stateChanged(Qt::WindowState)));
|
||||
connect(App::app(), SIGNAL(updateReady()), this, SLOT(showUpdateBtn()));
|
||||
|
||||
if (cPlatform() == dbipMac) {
|
||||
if (cPlatform() != dbipWindows) {
|
||||
_minimize.hide();
|
||||
_maximize.hide();
|
||||
_restore.hide();
|
||||
|
@ -127,7 +127,7 @@ TitleWidget::~TitleWidget() {
|
|||
void TitleWidget::resizeEvent(QResizeEvent *e) {
|
||||
QPoint p(width() - ((cPlatform() == dbipWindows && lastMaximized) ? 0 : st::sysBtnDelta), 0);
|
||||
|
||||
if (cPlatform() != dbipMac) {
|
||||
if (cPlatform() == dbipWindows) {
|
||||
p.setX(p.x() - _close.width());
|
||||
_close.move(p);
|
||||
|
||||
|
@ -216,7 +216,7 @@ HitTestType TitleWidget::hitTest(const QPoint &p) {
|
|||
if (x >= st::titleIconPos.x() && y >= st::titleIconPos.y() && x < st::titleIconPos.x() + st::titleIconRect.pxWidth() && y < st::titleIconPos.y() + st::titleIconRect.pxHeight()) {
|
||||
return HitTestIcon;
|
||||
} else if (false
|
||||
|| (_update.hitTest(p - _update.geometry().topLeft()) == HitTestSysButton) && _update.isVisible()
|
||||
|| (_update.hitTest(p - _update.geometry().topLeft()) == HitTestSysButton && _update.isVisible())
|
||||
|| (_minimize.hitTest(p - _minimize.geometry().topLeft()) == HitTestSysButton)
|
||||
|| (_maximize.hitTest(p - _maximize.geometry().topLeft()) == HitTestSysButton)
|
||||
|| (_restore.hitTest(p - _restore.geometry().topLeft()) == HitTestSysButton)
|
||||
|
|
|
@ -17,11 +17,10 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
|||
*/
|
||||
#include "stdafx.h"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#ifdef Q_OS_WIN
|
||||
#elif defined Q_OS_MAC
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
|
@ -175,8 +174,10 @@ namespace {
|
|||
_msStart = mach_absolute_time();
|
||||
#else
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
_msStart = 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec);
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
//_msFreq = 1 / 1000000.;
|
||||
_msgIdCoef = float64(0xFFFF0000L) / 1000000000.;
|
||||
_msStart = 1000 * uint64(ts.tv_sec) + (uint64(ts.tv_nsec) / 1000000);
|
||||
#endif
|
||||
|
||||
srand((uint32)(_msStart & 0xFFFFFFFFL));
|
||||
|
@ -217,13 +218,13 @@ uint64 getms() {
|
|||
return (uint64)((msCount - _msStart) * _msFreq);
|
||||
#else
|
||||
timespec ts;
|
||||
int res = clock_gettime(CLOCK_REALTIME, &ts);
|
||||
int res = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (res != 0) {
|
||||
LOG(("Bad clock_gettime result: %1").arg(res));
|
||||
return 0;
|
||||
}
|
||||
uint64 msCount = 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec);
|
||||
return (uint64)((msCount - _msStart) / 1000000);
|
||||
uint64 msCount = 1000 * uint64(ts.tv_sec) + (uint64(ts.tv_nsec) / 1000000);
|
||||
return (uint64)(msCount - _msStart);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -236,8 +237,10 @@ uint64 msgid() {
|
|||
uint64 msCount = mach_absolute_time();
|
||||
uint64 result = _msgIdStart + (uint64)floor((msCount - _msgIdMsStart) * _msgIdCoef);
|
||||
#else
|
||||
uint64 result = 0;
|
||||
//TODO
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
uint64 msCount = 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec);
|
||||
uint64 result = _msgIdStart + (uint64)floor((msCount - _msgIdMsStart) * _msgIdCoef);
|
||||
#endif
|
||||
|
||||
result &= ~0x03L;
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
QT += core gui widgets network multimedia
|
||||
QT += core gui network widgets
|
||||
#QT += multimedia
|
||||
|
||||
CONFIG += plugin static
|
||||
|
||||
CONFIG(debug, debug|release) {
|
||||
DEFINES += _DEBUG
|
||||
OBJECTS_DIR = ./../Mac/DebugIntermediate
|
||||
OBJECTS_DIR = ./../DebugIntermediate
|
||||
MOC_DIR = ./GeneratedFiles/Debug
|
||||
RCC_DIR = ./GeneratedFiles
|
||||
DESTDIR = ./../Mac/Debug
|
||||
DESTDIR = ./../Debug
|
||||
}
|
||||
CONFIG(release, debug|release) {
|
||||
OBJECTS_DIR = ./../Mac/ReleaseIntermediate
|
||||
DEFINES += CUSTOM_API_ID
|
||||
OBJECTS_DIR = ./../ReleaseIntermediate
|
||||
MOC_DIR = ./GeneratedFiles/Release
|
||||
RCC_DIR = ./GeneratedFiles
|
||||
DESTDIR = ./../Mac/Release
|
||||
DESTDIR = ./../Release
|
||||
}
|
||||
|
||||
macx {
|
||||
|
@ -21,6 +25,44 @@ macx {
|
|||
QMAKE_LFLAGS += -framework Cocoa
|
||||
}
|
||||
|
||||
linux {
|
||||
SOURCES += ./SourceFiles/pspecific_linux.cpp
|
||||
HEADERS += ./SourceFiles/pspecific_linux.h
|
||||
}
|
||||
|
||||
style_auto_cpp.target = ./../../Telegram/GeneratedFiles/style_auto.cpp
|
||||
style_auto_cpp.depends = FORCE
|
||||
style_auto_cpp.commands = ./../DebugStyle/MetaStyle -classes_in ./../../Telegram/Resources/style_classes.txt -classes_out ./../../Telegram/GeneratedFiles/style_classes.h -styles_in ./../../Telegram/Resources/style.txt -styles_out ./../../Telegram/GeneratedFiles/style_auto.h -path_to_sprites ./../../Telegram/SourceFiles/art/
|
||||
style_auto_cpp.depends = ./../../Telegram/Resources/style.txt ./../../Telegram/Resources/style_classes.txt
|
||||
|
||||
style_auto_h.target = ./../../Telegram/GeneratedFiles/style_auto.h
|
||||
style_auto_h.depends = FORCE
|
||||
style_auto_h.commands = ./../DebugStyle/MetaStyle -classes_in ./../../Telegram/Resources/style_classes.txt -classes_out ./../../Telegram/GeneratedFiles/style_classes.h -styles_in ./../../Telegram/Resources/style.txt -styles_out ./../../Telegram/GeneratedFiles/style_auto.h -path_to_sprites ./../../Telegram/SourceFiles/art/
|
||||
style_auto_h.depends = ./../../Telegram/Resources/style.txt ./../../Telegram/Resources/style_classes.txt
|
||||
|
||||
style_classes_h.target = ./../../Telegram/GeneratedFiles/style_classes.h
|
||||
style_classes_h.depends = FORCE
|
||||
style_classes_h.commands = ./../DebugStyle/MetaStyle -classes_in ./../../Telegram/Resources/style_classes.txt -classes_out ./../../Telegram/GeneratedFiles/style_classes.h -styles_in ./../../Telegram/Resources/style.txt -styles_out ./../../Telegram/GeneratedFiles/style_auto.h -path_to_sprites ./../../Telegram/SourceFiles/art/
|
||||
style_classes_h.depends = ./../../Telegram/Resources/style.txt ./../../Telegram/Resources/style_classes.txt
|
||||
|
||||
lang_cpp.target = ./../../Telegram/GeneratedFiles/lang.cpp
|
||||
lang_cpp.depends = FORCE
|
||||
lang_cpp.commands = ./../DebugLang/MetaLang -lang_in ./../../Telegram/Resources/lang.txt -lang_out ./../../Telegram/GeneratedFiles/lang
|
||||
lang_cpp.depends = ./../../Telegram/Resources/lang.txt
|
||||
|
||||
lang_h.target = ./../../Telegram/GeneratedFiles/lang.h
|
||||
lang_h.depends = FORCE
|
||||
lang_h.commands = ./../DebugLang/MetaLang -lang_in ./../../Telegram/Resources/lang.txt -lang_out ./../../Telegram/GeneratedFiles/lang
|
||||
lang_h.depends = ./../../Telegram/Resources/lang.txt
|
||||
|
||||
hook.depends = style_auto_cpp style_auto_h style_classes_h lang_cpp lang_h
|
||||
CONFIG(debug,debug|release):hook.target = Makefile.Debug
|
||||
CONFIG(release,debug|release):hook.target = Makefile.Release
|
||||
|
||||
QMAKE_EXTRA_TARGETS += style_auto_cpp style_auto_h style_classes_h lang_cpp lang_h hook
|
||||
|
||||
PRE_TARGETDEPS += ./GeneratedFiles/style_auto.cpp ./GeneratedFiles/style_auto.h ./GeneratedFiles/style_classes.h ./GeneratedFiles/lang.h ./GeneratedFiles/lang.cpp
|
||||
|
||||
SOURCES += \
|
||||
./SourceFiles/main.cpp \
|
||||
./SourceFiles/stdafx.cpp \
|
||||
|
@ -68,6 +110,7 @@ SOURCES += \
|
|||
./SourceFiles/gui/style_core.cpp \
|
||||
./SourceFiles/gui/text.cpp \
|
||||
./SourceFiles/gui/twidget.cpp \
|
||||
./SourceFiles/gui/switcher.cpp \
|
||||
./GeneratedFiles/lang.cpp \
|
||||
./GeneratedFiles/style_auto.cpp \
|
||||
./SourceFiles/boxes/aboutbox.cpp \
|
||||
|
@ -142,6 +185,7 @@ HEADERS += \
|
|||
./SourceFiles/gui/style_core.h \
|
||||
./SourceFiles/gui/text.h \
|
||||
./SourceFiles/gui/twidget.h \
|
||||
./SourceFiles/gui/switcher.h \
|
||||
./GeneratedFiles/lang.h \
|
||||
./GeneratedFiles/style_auto.h \
|
||||
./GeneratedFiles/style_classes.h \
|
||||
|
@ -180,17 +224,19 @@ CONFIG += precompile_header
|
|||
|
||||
PRECOMPILED_HEADER = ./SourceFiles/stdafx.h
|
||||
|
||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.0/QtGui\
|
||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.0/QtCore\
|
||||
QMAKE_CXXFLAGS += -fno-strict-aliasing
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter -Wno-unused-variable -Wno-switch -Wno-comment -Wno-unused-but-set-variable
|
||||
|
||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.1/QtGui\
|
||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.1/QtCore\
|
||||
./../../Libraries/QtStatic/qtbase/include\
|
||||
./SourceFiles\
|
||||
./GeneratedFiles\
|
||||
./../../Libraries/lzma/C\
|
||||
./../../Libraries/libexif-0.6.20
|
||||
|
||||
LIBS += -lcrypto -lssl -lz
|
||||
LIBS += ./../../Libraries/libexif-0.6.20/libexif/.libs/libexif.a
|
||||
|
||||
./../../Libraries/libexif-0.6.20\
|
||||
/usr/local/ssl/include
|
||||
LIBS += -L/usr/local/ssl/lib -lcrypto -lssl -lz -ldl -llzma
|
||||
LIBS += ./../../../Libraries/libexif-0.6.20/libexif/.libs/libexif.a
|
||||
LIBS += ./../../../Libraries/QtStatic/qtmultimedia/plugins/audio/libqtmedia_pulse.a
|
||||
RESOURCES += \
|
||||
./SourceFiles/telegram.qrc
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
TEMPLATE = app
|
||||
CONFIG += console
|
||||
CONFIG -= app_bundle
|
||||
CONFIG -= qt
|
||||
|
||||
SOURCES += ./SourceFiles/_other/updater_linux.cpp
|
||||
|
||||
CONFIG(debug, debug|release) {
|
||||
DEFINES += _DEBUG
|
||||
OBJECTS_DIR = ./../DebugIntermediateUpdater
|
||||
DESTDIR = ./../Debug
|
||||
}
|
||||
CONFIG(release, debug|release) {
|
||||
DEFINES += CUSTOM_API_ID
|
||||
OBJECTS_DIR = ./../ReleaseIntermediateUpdater
|
||||
DESTDIR = ./../Release
|
||||
}
|
|
@ -0,0 +1,490 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qbasicfontdatabase_p.h"
|
||||
|
||||
#include <QtGui/private/qguiapplication_p.h>
|
||||
#include <qpa/qplatformscreen.h>
|
||||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QLibraryInfo>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtCore/QtEndian>
|
||||
|
||||
#undef QT_NO_FREETYPE
|
||||
#include <QtGui/private/qfontengine_ft_p.h>
|
||||
#include <QtGui/private/qfontengine_p.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_TRUETYPE_TABLES_H
|
||||
#include FT_ERRORS_H
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
typedef struct {
|
||||
quint16 majorVersion;
|
||||
quint16 minorVersion;
|
||||
quint16 numTables;
|
||||
quint16 searchRange;
|
||||
quint16 entrySelector;
|
||||
quint16 rangeShift;
|
||||
} OFFSET_TABLE;
|
||||
|
||||
typedef struct {
|
||||
quint32 tag;
|
||||
quint32 checkSum;
|
||||
quint32 offset;
|
||||
quint32 length;
|
||||
} TABLE_DIRECTORY;
|
||||
|
||||
typedef struct {
|
||||
quint16 fontSelector;
|
||||
quint16 nrCount;
|
||||
quint16 storageOffset;
|
||||
} NAME_TABLE_HEADER;
|
||||
|
||||
typedef struct {
|
||||
quint16 platformID;
|
||||
quint16 encodingID;
|
||||
quint16 languageID;
|
||||
quint16 nameID;
|
||||
quint16 stringLength;
|
||||
quint16 stringOffset;
|
||||
} NAME_RECORD;
|
||||
|
||||
void QBasicFontDatabase::populateFontDatabase()
|
||||
{
|
||||
QString fontpath = fontDir();
|
||||
|
||||
if(!QFile::exists(fontpath)) {
|
||||
qFatal("QFontDatabase: Cannot find font directory %s - is Qt installed correctly?",
|
||||
qPrintable(fontpath));
|
||||
}
|
||||
|
||||
QDir dir(fontpath);
|
||||
dir.setNameFilters(QStringList() << QLatin1String("*.ttf")
|
||||
<< QLatin1String("*.ttc") << QLatin1String("*.pfa")
|
||||
<< QLatin1String("*.pfb")
|
||||
<< QLatin1String("*.otf"));
|
||||
dir.refresh();
|
||||
for (int i = 0; i < int(dir.count()); ++i) {
|
||||
const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
|
||||
// qDebug() << "looking at" << file;
|
||||
addTTFile(QByteArray(), file);
|
||||
}
|
||||
}
|
||||
|
||||
QFontEngine *QBasicFontDatabase::fontEngine(const QFontDef &fontDef, void *usrPtr)
|
||||
{
|
||||
FontFile *fontfile = static_cast<FontFile *> (usrPtr);
|
||||
QFontEngine::FaceId fid;
|
||||
fid.filename = QFile::encodeName(fontfile->fileName);
|
||||
fid.index = fontfile->indexValue;
|
||||
|
||||
bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
|
||||
QFontEngineFT::GlyphFormat format = antialias? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono;
|
||||
|
||||
QFontEngineFT *engine = new QFontEngineFT(fontDef);
|
||||
if (!engine->init(fid, antialias, format) || engine->invalid()) {
|
||||
delete engine;
|
||||
engine = 0;
|
||||
}
|
||||
|
||||
return engine;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class QFontEngineFTRawData: public QFontEngineFT
|
||||
{
|
||||
public:
|
||||
QFontEngineFTRawData(const QFontDef &fontDef) : QFontEngineFT(fontDef)
|
||||
{
|
||||
}
|
||||
|
||||
void updateFamilyNameAndStyle()
|
||||
{
|
||||
fontDef.family = QString::fromLatin1(freetype->face->family_name);
|
||||
|
||||
if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)
|
||||
fontDef.style = QFont::StyleItalic;
|
||||
|
||||
if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD)
|
||||
fontDef.weight = QFont::Bold;
|
||||
}
|
||||
|
||||
bool initFromData(const QByteArray &fontData)
|
||||
{
|
||||
FaceId faceId;
|
||||
faceId.filename = "";
|
||||
faceId.index = 0;
|
||||
faceId.uuid = QUuid::createUuid().toByteArray();
|
||||
|
||||
return init(faceId, true, Format_None, fontData);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
QFontEngine *QBasicFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize,
|
||||
QFont::HintingPreference hintingPreference)
|
||||
{
|
||||
QFontDef fontDef;
|
||||
fontDef.pixelSize = pixelSize;
|
||||
fontDef.hintingPreference = hintingPreference;
|
||||
|
||||
QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef);
|
||||
if (!fe->initFromData(fontData)) {
|
||||
delete fe;
|
||||
return 0;
|
||||
}
|
||||
|
||||
fe->updateFamilyNameAndStyle();
|
||||
|
||||
switch (hintingPreference) {
|
||||
case QFont::PreferNoHinting:
|
||||
fe->setDefaultHintStyle(QFontEngineFT::HintNone);
|
||||
break;
|
||||
case QFont::PreferFullHinting:
|
||||
fe->setDefaultHintStyle(QFontEngineFT::HintFull);
|
||||
break;
|
||||
case QFont::PreferVerticalHinting:
|
||||
fe->setDefaultHintStyle(QFontEngineFT::HintLight);
|
||||
break;
|
||||
default:
|
||||
// Leave it as it is
|
||||
break;
|
||||
}
|
||||
|
||||
return fe;
|
||||
}
|
||||
|
||||
QStringList QBasicFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
|
||||
{
|
||||
Q_UNUSED(family);
|
||||
Q_UNUSED(style);
|
||||
Q_UNUSED(script);
|
||||
Q_UNUSED(styleHint);
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QStringList QBasicFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName)
|
||||
{
|
||||
return addTTFile(fontData,fileName.toLocal8Bit());
|
||||
}
|
||||
|
||||
void QBasicFontDatabase::releaseHandle(void *handle)
|
||||
{
|
||||
FontFile *file = static_cast<FontFile *>(handle);
|
||||
delete file;
|
||||
}
|
||||
|
||||
extern FT_Library qt_getFreetype();
|
||||
|
||||
// copied from freetype with some modifications
|
||||
|
||||
#ifndef FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY
|
||||
#define FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY FT_MAKE_TAG('i', 'g', 'p', 'f')
|
||||
#endif
|
||||
|
||||
#ifndef FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY
|
||||
#define FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY FT_MAKE_TAG('i', 'g', 'p', 's')
|
||||
#endif
|
||||
|
||||
/* there's a Mac-specific extended implementation of FT_New_Face() */
|
||||
/* in src/base/ftmac.c */
|
||||
|
||||
#if !defined( FT_MACINTOSH ) || defined( DARWIN_NO_CARBON )
|
||||
|
||||
/* documentation is in freetype.h */
|
||||
|
||||
FT_Error __ft_New_Face(FT_Library library, const char* pathname, FT_Long face_index, FT_Face *aface) {
|
||||
FT_Open_Args args;
|
||||
|
||||
/* test for valid `library' and `aface' delayed to FT_Open_Face() */
|
||||
if ( !pathname )
|
||||
return FT_Err_Invalid_Argument;
|
||||
|
||||
FT_Parameter params[2];
|
||||
params[0].tag = FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY;
|
||||
params[0].data = 0;
|
||||
params[1].tag = FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY;
|
||||
params[1].data = 0;
|
||||
args.flags = FT_OPEN_PATHNAME | FT_OPEN_PARAMS;
|
||||
args.pathname = (char*)pathname;
|
||||
args.stream = NULL;
|
||||
args.num_params = 2;
|
||||
args.params = params;
|
||||
|
||||
return FT_Open_Face( library, &args, face_index, aface );
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
FT_Error __ft_New_Face(FT_Library library, const char* pathname, FT_Long face_index, FT_Face *aface) {
|
||||
return FT_New_Face(library, pathname, face_index, aface);
|
||||
}
|
||||
|
||||
#endif /* defined( FT_MACINTOSH ) && !defined( DARWIN_NO_CARBON ) */
|
||||
|
||||
/* documentation is in freetype.h */
|
||||
|
||||
FT_Error __ft_New_Memory_Face(FT_Library library, const FT_Byte* file_base, FT_Long file_size, FT_Long face_index, FT_Face *aface) {
|
||||
FT_Open_Args args;
|
||||
|
||||
/* test for valid `library' and `face' delayed to FT_Open_Face() */
|
||||
if ( !file_base )
|
||||
return FT_Err_Invalid_Argument;
|
||||
|
||||
FT_Parameter params[2];
|
||||
params[0].tag = FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY;
|
||||
params[0].data = 0;
|
||||
params[1].tag = FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY;
|
||||
params[1].data = 0;
|
||||
args.flags = FT_OPEN_MEMORY | FT_OPEN_PARAMS;
|
||||
args.memory_base = file_base;
|
||||
args.memory_size = file_size;
|
||||
args.stream = NULL;
|
||||
args.num_params = 2;
|
||||
args.params = params;
|
||||
|
||||
return FT_Open_Face( library, &args, face_index, aface );
|
||||
}
|
||||
|
||||
// end
|
||||
|
||||
QStringList QBasicFontDatabase::addTTFile(const QByteArray &fontData, const QByteArray &file, QSupportedWritingSystems *supportedWritingSystems)
|
||||
{
|
||||
FT_Library library = qt_getFreetype();
|
||||
|
||||
int index = 0;
|
||||
int numFaces = 0;
|
||||
QStringList families;
|
||||
do {
|
||||
FT_Face face;
|
||||
FT_Error error;
|
||||
if (!fontData.isEmpty()) {
|
||||
error = __ft_New_Memory_Face(library, (const FT_Byte *)fontData.constData(), fontData.size(), index, &face);
|
||||
} else {
|
||||
error = __ft_New_Face(library, file.constData(), index, &face);
|
||||
}
|
||||
if (error != FT_Err_Ok) {
|
||||
qDebug() << "FT_New_Face failed with index" << index << ":" << hex << error;
|
||||
break;
|
||||
}
|
||||
numFaces = face->num_faces;
|
||||
|
||||
QFont::Weight weight = QFont::Normal;
|
||||
|
||||
QFont::Style style = QFont::StyleNormal;
|
||||
if (face->style_flags & FT_STYLE_FLAG_ITALIC)
|
||||
style = QFont::StyleItalic;
|
||||
|
||||
if (face->style_flags & FT_STYLE_FLAG_BOLD)
|
||||
weight = QFont::Bold;
|
||||
|
||||
bool fixedPitch = (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH);
|
||||
|
||||
QSupportedWritingSystems writingSystems;
|
||||
// detect symbol fonts
|
||||
for (int i = 0; i < face->num_charmaps; ++i) {
|
||||
FT_CharMap cm = face->charmaps[i];
|
||||
if (cm->encoding == FT_ENCODING_ADOBE_CUSTOM
|
||||
|| cm->encoding == FT_ENCODING_MS_SYMBOL) {
|
||||
writingSystems.setSupported(QFontDatabase::Symbol);
|
||||
if (supportedWritingSystems)
|
||||
supportedWritingSystems->setSupported(QFontDatabase::Symbol);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
|
||||
if (os2) {
|
||||
quint32 unicodeRange[4] = {
|
||||
quint32(os2->ulUnicodeRange1),
|
||||
quint32(os2->ulUnicodeRange2),
|
||||
quint32(os2->ulUnicodeRange3),
|
||||
quint32(os2->ulUnicodeRange4)
|
||||
};
|
||||
quint32 codePageRange[2] = {
|
||||
quint32(os2->ulCodePageRange1),
|
||||
quint32(os2->ulCodePageRange2)
|
||||
};
|
||||
|
||||
writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
|
||||
if (supportedWritingSystems)
|
||||
*supportedWritingSystems = writingSystems;
|
||||
|
||||
if (os2->usWeightClass == 0)
|
||||
;
|
||||
else if (os2->usWeightClass < 350)
|
||||
weight = QFont::Light;
|
||||
else if (os2->usWeightClass < 450)
|
||||
weight = QFont::Normal;
|
||||
else if (os2->usWeightClass < 650)
|
||||
weight = QFont::DemiBold;
|
||||
else if (os2->usWeightClass < 750)
|
||||
weight = QFont::Bold;
|
||||
else if (os2->usWeightClass < 1000)
|
||||
weight = QFont::Black;
|
||||
|
||||
if (os2->panose[2] >= 2) {
|
||||
int w = os2->panose[2];
|
||||
if (w <= 3)
|
||||
weight = QFont::Light;
|
||||
else if (w <= 5)
|
||||
weight = QFont::Normal;
|
||||
else if (w <= 7)
|
||||
weight = QFont::DemiBold;
|
||||
else if (w <= 8)
|
||||
weight = QFont::Bold;
|
||||
else if (w <= 10)
|
||||
weight = QFont::Black;
|
||||
}
|
||||
}
|
||||
|
||||
QString family = QString::fromLatin1(face->family_name);
|
||||
FontFile *fontFile = new FontFile;
|
||||
fontFile->fileName = QFile::decodeName(file);
|
||||
fontFile->indexValue = index;
|
||||
|
||||
QFont::Stretch stretch = QFont::Unstretched;
|
||||
|
||||
registerFont(family,QString::fromLatin1(face->style_name),QString(),weight,style,stretch,true,true,0,fixedPitch,writingSystems,fontFile);
|
||||
|
||||
families.append(family);
|
||||
|
||||
FT_Done_Face(face);
|
||||
++index;
|
||||
} while (index < numFaces);
|
||||
return families;
|
||||
}
|
||||
|
||||
QString QBasicFontDatabase::fontNameFromTTFile(const QString &filename)
|
||||
{
|
||||
QFile f(filename);
|
||||
QString retVal;
|
||||
qint64 bytesRead;
|
||||
qint64 bytesToRead;
|
||||
|
||||
if (f.open(QIODevice::ReadOnly)) {
|
||||
OFFSET_TABLE ttOffsetTable;
|
||||
bytesToRead = sizeof(OFFSET_TABLE);
|
||||
bytesRead = f.read((char*)&ttOffsetTable, bytesToRead);
|
||||
if (bytesToRead != bytesRead)
|
||||
return retVal;
|
||||
ttOffsetTable.numTables = qFromBigEndian(ttOffsetTable.numTables);
|
||||
ttOffsetTable.majorVersion = qFromBigEndian(ttOffsetTable.majorVersion);
|
||||
ttOffsetTable.minorVersion = qFromBigEndian(ttOffsetTable.minorVersion);
|
||||
|
||||
if (ttOffsetTable.majorVersion != 1 || ttOffsetTable.minorVersion != 0)
|
||||
return retVal;
|
||||
|
||||
TABLE_DIRECTORY tblDir;
|
||||
bool found = false;
|
||||
|
||||
for (int i = 0; i < ttOffsetTable.numTables; i++) {
|
||||
bytesToRead = sizeof(TABLE_DIRECTORY);
|
||||
bytesRead = f.read((char*)&tblDir, bytesToRead);
|
||||
if (bytesToRead != bytesRead)
|
||||
return retVal;
|
||||
if (qFromBigEndian(tblDir.tag) == MAKE_TAG('n', 'a', 'm', 'e')) {
|
||||
found = true;
|
||||
tblDir.length = qFromBigEndian(tblDir.length);
|
||||
tblDir.offset = qFromBigEndian(tblDir.offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
f.seek(tblDir.offset);
|
||||
NAME_TABLE_HEADER ttNTHeader;
|
||||
bytesToRead = sizeof(NAME_TABLE_HEADER);
|
||||
bytesRead = f.read((char*)&ttNTHeader, bytesToRead);
|
||||
if (bytesToRead != bytesRead)
|
||||
return retVal;
|
||||
ttNTHeader.nrCount = qFromBigEndian(ttNTHeader.nrCount);
|
||||
ttNTHeader.storageOffset = qFromBigEndian(ttNTHeader.storageOffset);
|
||||
NAME_RECORD ttRecord;
|
||||
found = false;
|
||||
|
||||
for (int i = 0; i < ttNTHeader.nrCount; i++) {
|
||||
bytesToRead = sizeof(NAME_RECORD);
|
||||
bytesRead = f.read((char*)&ttRecord, bytesToRead);
|
||||
if (bytesToRead != bytesRead)
|
||||
return retVal;
|
||||
ttRecord.nameID = qFromBigEndian(ttRecord.nameID);
|
||||
if (ttRecord.nameID == 1) {
|
||||
ttRecord.stringLength = qFromBigEndian(ttRecord.stringLength);
|
||||
ttRecord.stringOffset = qFromBigEndian(ttRecord.stringOffset);
|
||||
int nPos = f.pos();
|
||||
f.seek(tblDir.offset + ttRecord.stringOffset + ttNTHeader.storageOffset);
|
||||
|
||||
QByteArray nameByteArray = f.read(ttRecord.stringLength);
|
||||
if (!nameByteArray.isEmpty()) {
|
||||
if (ttRecord.encodingID == 256 || ttRecord.encodingID == 768) {
|
||||
//This is UTF-16 in big endian
|
||||
int stringLength = ttRecord.stringLength / 2;
|
||||
retVal.resize(stringLength);
|
||||
QChar *data = retVal.data();
|
||||
const ushort *srcData = (const ushort *)nameByteArray.data();
|
||||
for (int i = 0; i < stringLength; ++i)
|
||||
data[i] = qFromBigEndian(srcData[i]);
|
||||
return retVal;
|
||||
} else if (ttRecord.encodingID == 0) {
|
||||
//This is Latin1
|
||||
retVal = QString::fromLatin1(nameByteArray);
|
||||
} else {
|
||||
qWarning("Could not retrieve Font name from file: %s", qPrintable(QDir::toNativeSeparators(filename)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
f.seek(nPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
Loading…
Reference in New Issue