#include "ccnotepad.h" #include "nddsetting.h" #include "styleset.h" #include #include #include #include #include #include #include #include #include #ifdef Q_OS_UNIX #include #include #include #include #include #endif #ifdef Q_OS_WIN #pragma comment(lib, "user32.lib") #if _DEBUG #pragma comment(lib, "qmyedit_qt5d.lib") #else #pragma comment(lib, "qmyedit_qt5.lib") #endif #include const ULONG_PTR CUSTOM_TYPE = 10000; const ULONG_PTR OPEN_NOTEPAD_TYPE = 10001; const ULONG_PTR CUSTOM_TYPE_FILE_LINENUM = 10002; bool s_isAdminAuth = false; #endif const QString c_strTitle = "Ndd"; #ifdef Q_OS_UNIX #if defined(Q_OS_MAC) QSharedMemory shared("CCNotebook123");;//mac下面后面带一个版本号,避免新的打不开 #else QSharedMemory shared("CCNotebook"); #endif QSharedMemory nppShared("notepad--"); static void sig_usr(int signo) { if(signo == SIGUSR1) { qlonglong winId; shared.lock(); memcpy(&winId,shared.data(),sizeof(qlonglong)); shared.unlock(); QWidget *pMain = QWidget::find((WId)winId); CCNotePad* pNotePad = dynamic_cast(pMain); if(pNotePad != nullptr) { QString filePath((char*)nppShared.data()+4); if(!filePath.isEmpty()) { pNotePad->openFile(filePath); } pNotePad->activateWindow(); pNotePad->showNormal(); qDebug() << "sig_usr" << filePath; } } } #endif #ifdef Q_OS_MAC static void openfile(QString filePath) { qlonglong winId; shared.lock(); memcpy(&winId,shared.data(),sizeof(qlonglong)); shared.unlock(); QWidget *pMain = QWidget::find((WId)winId); CCNotePad* pNotePad = dynamic_cast(pMain); if(pNotePad != nullptr) { if(!filePath.isEmpty()) { pNotePad->openFile(filePath); } pNotePad->activateWindow(); pNotePad->showNormal(); } } class MyApplication : public QApplication { public: MyApplication(int &argc, char **argv) : QApplication(argc, argv) { } bool event(QEvent *event) { if (event->type() == QEvent::FileOpen) { QFileOpenEvent *openEvent = static_cast(event); qDebug() << "Open file" << openEvent->file(); s_openfile = openEvent->file(); openfile(s_openfile); } return QApplication::event(event); } QString s_openfile; }; #endif int main(int argc, char *argv[]) { //可以防止某些屏幕下的字体拥挤重叠问题。暂时屏蔽,不使用qt方法,使用windows自带方案 // 发现windows自带方案模糊。//发现下面打开后,在win10上反而效果不好,界面会变得很大,默认还是不开启的好。 //QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); //QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); //QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); #ifdef Q_OS_MAC MyApplication a(argc, argv); #else QApplication a(argc, argv); #endif //不能开启,开启后相对路径打开文件失败 //QDir::setCurrent(QCoreApplication::applicationDirPath()); #if defined(Q_OS_UNIX) QApplication::setStyle(QStyleFactory::create("fusion")); #endif a.setApplicationDisplayName(c_strTitle); a.setApplicationName(c_strTitle); QStringList arguments = QCoreApplication::arguments(); //目前就三种 //1) ndd filepath //2) ndd filepath -n linenum //3) ndd -multi filepath //只有 1 2 需要处理短路径 if ((arguments.size() == 2) || (arguments.size() == 4)) { QFileInfo fi(arguments[1]); if (fi.isRelative()) { QString absDir = QDir::currentPath(); //获取绝对路径 arguments[1] = QString("%1/%2").arg(absDir).arg(arguments.at(1)); } } #ifdef uos QFont font("Noto Sans CJK SC,9,-1,5,50,0,0,0,0,0,Regular", 9); QApplication::setFont(font); #endif #ifdef Q_OS_MAC //这里的字体大小,务必要和查找结果框的高度匹配,否则会结构字体拥挤 QFont font("Courier New,11,-1,5,50,0,0,0,0,0,Regular", 11); // qDebug() << "font name mac"; QApplication::setFont(font); // qDebug() << QApplication::font().toString(); #endif bool isGotoLine = false; #ifdef Q_OS_WIN QSharedMemory shared("ccnotepad"); if (arguments.size() > 2) { //如果是多开请求,这种是从管理员权限申请后重开过来的 if (arguments[1] == QString("-muti")) { s_isAdminAuth = true; QString title = QString(u8"%1 管理员").arg(c_strTitle); a.setApplicationDisplayName(title); //删除-muti这个参数 arguments.removeAt(1); //管理员不占用共享标志。这样的目的是,当管理员窗口存在时 //打开原来的文件,原来的文件可以占用共享标志,作为主窗口打开。 //管理员窗口永远不做主窗口打开 goto authAdmin; } else if ((arguments.size() == 4) && arguments[2] == QString("-n")) { //使用的是 file -n lineNums 方式。目前只有windows下支持 xxxfile -n linenum的格式 isGotoLine = true; } } #else if ((arguments.size() == 4) && (arguments[2] == QString("-n"))) { //使用的是 file -n lineNums 方式。目前只有windows下支持 xxxfile -n linenum的格式 isGotoLine = true; } #endif //attach成功表示已经存在该内存了,表示当前存在实例 if (shared.attach())//共享内存被占用则直接返回 { //发现在文件中如果存在空格时,参数不止1个,所以不能单纯用2个参数表示 if (arguments.size() > 1) { #if defined(Q_OS_WIN) int tryTimes = 0; do { qlonglong hwndId; shared.lock(); memcpy(&hwndId, shared.data(), sizeof(qlonglong)); shared.unlock(); HWND hwnd = (HWND)hwndId; if (::IsWindow(hwnd)) { if (!isGotoLine) { //就是ndd filepath的命令行格式 //去掉第一个参数,后续的参数拼接起来。其实参数中间有空格还是需要使用""引用起来,避免空格参数分隔为多个 arguments.takeFirst(); QString filename = arguments.join(""); QByteArray data = filename.toUtf8(); COPYDATASTRUCT copydata; copydata.dwData = CUSTOM_TYPE; //自定义类型 copydata.lpData = data.data(); //数据大小 copydata.cbData = data.size(); // 指向数据的指针 ::SendMessage(hwnd, WM_COPYDATA, reinterpret_cast(nullptr), reinterpret_cast(©data)); } else { //是 filepath -n linenums 方式。不考虑filepath含有空格的情况,因为前面做了严格判断 QString para = QString("%1|%2").arg(arguments[1]).arg(arguments[3]); QByteArray data = para.toUtf8(); COPYDATASTRUCT copydata; copydata.dwData = CUSTOM_TYPE_FILE_LINENUM; //自定义类型 copydata.lpData = data.data(); //数据大小 copydata.cbData = data.size(); // 指向数据的指针 ::SendMessage(hwnd, WM_COPYDATA, reinterpret_cast(nullptr), reinterpret_cast(©data)); } break; } else { //20230304 右键多个文件同时打开,比如3个。此时只有第1个可获取锁,其余2个均走这里。 //因为第个还没有来的及写入hwnd。此时不要goto drop_old。等一下再重试 QThread::sleep(1); ++tryTimes; //2次识别后,没法了,只能通过继续往下走。 //失败了,此时说明前一个窗口极可能状态错误了。如果不处理,则再也打不开程序了 if (tryTimes > 2) { goto drop_old; } } } while (true); #elif defined (Q_OS_MAC) { //mac下面不需要,有他自身的机制保证 } #else pid_t pid; arguments.takeFirst(); QString filename = arguments.join(""); QByteArray data = filename.toUtf8(); nppShared.attach(); nppShared.lock(); memcpy(&pid, nppShared.data(), sizeof(pid_t)); memset((char*)nppShared.data()+sizeof(pid_t),0, 1024-sizeof(pid_t)); memcpy((char*)nppShared.data()+sizeof(pid_t),data.data(),data.size()); nppShared.unlock(); //if kill failed, then open a new process if(0 != kill(pid,SIGUSR1)) { goto unix_goon; } #endif } else if (arguments.size() == 1) { #if defined(Q_OS_WIN) //把窗口设置到最前 qlonglong hwndId; shared.lock(); memcpy(&hwndId, shared.data(), sizeof(qlonglong)); shared.unlock(); HWND hwnd = (HWND)hwndId; if (::IsWindow(hwnd)) { QString filename("open"); QByteArray data = filename.toUtf8(); COPYDATASTRUCT copydata; copydata.dwData = OPEN_NOTEPAD_TYPE; //自定义类型 copydata.lpData = data.data(); //数据大小 copydata.cbData = data.size(); // 指向数据的指针 ::SendMessage(hwnd, WM_COPYDATA, reinterpret_cast(nullptr), reinterpret_cast(©data)); } else { //失败了,此时说明前一个窗口极可能状态错误了。如果不处理,则再也打不开程序了 //继续新开一个窗口,放弃之前的旧内容 goto drop_old; } #elif defined (Q_OS_MAC) { //mac下面不需要,有他自身的机制保证 } #else pid_t pid; nppShared.attach(); nppShared.lock(); memcpy(&pid, nppShared.data(), sizeof(pid_t)); memset((char*)nppShared.data()+sizeof(pid_t),0, 1024-sizeof(pid_t)); nppShared.unlock(); qDebug()<<"empty file send"; if(0 != kill(pid,SIGUSR1)) { goto unix_goon; } #endif } return 0; } #if defined(Q_OS_WIN) shared.create(32); #elif defined (Q_OS_MAC) { //mac下面不需要,有他自身的机制保证。当程序已经在线时,再打开程序,系统会自动调用已经存在的程序出现 //不需要使用类似linux下面的机制。 shared.create(32); nppShared.create(32); } #else unix_goon: shared.create(32); nppShared.create(2048); if(signal(SIGUSR1,sig_usr) == SIG_ERR) { qDebug()<<"linux create sign failed"; } #endif #if defined(Q_OS_WIN) authAdmin: drop_old: #endif //20221009发现有小概率出现窗口没有,但是进程还在的诡异问题,加个保护一下 QApplication::setQuitOnLastWindowClosed(true); NddSetting::init(); int id = NddSetting::getKeyValueFromNumSets(SKIN_KEY); StyleSet::setSkin(id); CCNotePad *pMainNotepad = new CCNotePad(true); pMainNotepad->setAttribute(Qt::WA_DeleteOnClose); pMainNotepad->setShareMem(&shared); pMainNotepad->quickshow(); pMainNotepad->syncCurSkinToMenu(id); #ifdef Q_OS_WIN //HWND hwnd = ::FindWindowA("Qt5QWindowIcon", "CCNotebook"); //发现hwnd就是和effectiveWinId相等的,不需要查询了 //管理员可以多开,暂时不把管理员的权限作为主窗口,因为其他用户没有权限右键菜单发送消息给管理员窗口去打开文件 if (!s_isAdminAuth) { qlonglong winId = (qlonglong)pMainNotepad->effectiveWinId(); shared.lock(); memcpy(shared.data(), &winId, sizeof(qlonglong)); shared.unlock(); } #else qlonglong winId = (qlonglong)pMainNotepad->effectiveWinId(); shared.lock(); memcpy(shared.data(), &winId, sizeof(qlonglong)); shared.unlock(); nppShared.attach(); //get proceess id to share memory pid_t pid = getpid(); nppShared.lock(); memcpy(nppShared.data(), &pid, sizeof(pid_t)); nppShared.unlock(); #endif // Q_OS_WIN //恢复上次关闭时的文件 #ifdef Q_OS_WIN if (!s_isAdminAuth) { if (0 == pMainNotepad->restoreLastFiles() && (arguments.size() == 1)) { pMainNotepad->initTabNewOne(); } } #else if (0 == pMainNotepad->restoreLastFiles()) { pMainNotepad->initTabNewOne(); } #endif if (arguments.size() == 2) { #ifdef Q_OS_WIN if (!s_isAdminAuth) { pMainNotepad->openFile(arguments[1]); } else { //如果是管理员,还不能直接打开文件,需要恢复之前文件的修改内容 //恢复不了,再直接打开 pMainNotepad->tryRestoreFile(arguments[1]); } #else pMainNotepad->openFile(arguments[1]); #endif } else if (isGotoLine) { //是filepath -n xxx 格式。 bool ok = true; int lineNum = arguments[3].toInt(&ok); if (!ok) { lineNum = -1; } pMainNotepad->openFile(arguments[1], lineNum); } #ifdef Q_OS_WIN pMainNotepad->checkAppFont(); #endif a.exec(); NddSetting::close(); return 0; }