merge conflicts fixed

This commit is contained in:
John Preston 2014-07-11 11:04:58 +04:00
commit 7fd819dc0f
25 changed files with 2091 additions and 86 deletions

4
.gitignore vendored
View File

@ -25,3 +25,7 @@ ipch/
/Mac/
/Telegram/*.xcodeproj/xcuserdata/
/Telegram/*.xcodeproj/project.xcworkspace/
/Telegram/*.user.*
/Linux/
/Telegram/Makefile

View File

@ -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\

View File

@ -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\

View File

@ -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\

View File

@ -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

View File

@ -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;
}

View File

@ -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() {

View File

@ -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) {

View File

@ -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());

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}
}/**/

View File

@ -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
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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

View File

@ -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) {
}

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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

17
Telegram/Updater.pro Normal file
View File

@ -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
}

View File

@ -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