/** * Adds a participant to a convo, if that conversation dont exist, the tab and convo is created. * @brief MainWindow::addToConvo * @param fromIp ip of user that added someone to a conversation, empty if originating user is the current user. * @param fromName name of user that added someone to a conversation, empty if originating user is the current user. * @param member new participant to be added: "{name}/{ip}" * @param cid Conversation id. * @return false if member already exists in the conversation, true otherwise. */ bool MainWindow::addToConvo(QString fromIp, QString fromName, QString member, QString cid) { Conversation* lst = 0; QStringList tmp = member.split("/", QString::SkipEmptyParts); int i = 0; for (; i < convos->count(); ++i) { if(convos->at(i)->getCid().compare(cid) == 0) { lst = convos->at(i); break; } } if(lst == 0) { createTab(cid, fromIp, fromName); lst = convos->at(convos->count()-1); } else { for (int i = 0; i < lst->count(); ++i) { if(tmp.at(1).compare(lst->at(i)->getIp()) == 0) return false; } } lst->add(tmp.at(1), tmp.at(0)); if(ui->tabgrpConversations->currentIndex() == (i+1)) ui->lstInConvo->addItem(member); return true; }
void ConversationManager::handleMessageReceived(Swift::Message::ref message) { // std::string name = message->getTo().getUnescapedNode(); // if (name.find_last_of("%") != std::string::npos) { // OK when commented // name.replace(name.find_last_of("%"), 1, "@"); // OK when commented // } std::string name = Buddy::JIDToLegacyName(message->getTo()); if (name.empty()) { LOG4CXX_WARN(logger, m_user->getJID().toString() << ": Tried to create empty conversation"); return; } // create conversation if it does not exist. if (!m_convs[name]) { Conversation *conv = m_component->getFactory()->createConversation(this, name); addConversation(conv); } // if it exists and it's MUC, but this message is PM, get PM conversation or create new one. else if (m_convs[name]->isMUC() && message->getType() != Swift::Message::Groupchat) { std::string room_name = name; name = room_name + "/" + message->getTo().getResource(); if (m_convs.find(name) == m_convs.end()) { Conversation *conv = m_component->getFactory()->createConversation(this, message->getTo().getResource()); conv->setRoom(room_name); conv->setNickname(name); addConversation(conv); } } // update resource and send the message m_convs[name]->setJID(message->getFrom()); m_convs[name]->sendMessage(message); }
Conversation LoadConversation() { Conversation conversation; DataFile file(cin); for(const DataNode &node : file) if(node.Token(0) == "conversation") { conversation.Load(node); break; } const map<string, string> subs = { {"<bunks>", "[N]"}, {"<cargo>", "[N tons of Commodity]"}, {"<commodity>", "[Commodity]"}, {"<date>", "[Day Mon Year]"}, {"<day>", "[The Nth of Month]"}, {"<destination>", "[Planet in the Star system]"}, {"<fare>", "[N passengers]"}, {"<first>", "[First]"}, {"<last>", "[Last]"}, {"<origin>", "[Origin Planet]"}, {"<passengers>", "[your passengers]"}, {"<planet>", "[Planet]"}, {"<ship>", "[Ship]"}, {"<system>", "[Star]"}, {"<tons>", "[N tons]"} }; return conversation.Substitute(subs); }
/** * Adds selected participant to a conversation, and notifies participants. * @brief MainWindow::contextAddToConvo * @param sel Member to add, formatted: "{name}/{ip}" */ void MainWindow::contextAddToConvo(QString sel) { Conversation* convo = convos->at(ui->tabgrpConversations->currentIndex()-1); if(addToConvo("","",sel, convo->getCid())) { controller->notifyMembersChanged(convo, sel); } }
/** * Called when user has been added to an existing conversation * between two or more participants. Creates a new tab, and * adds all participants to the corresponding conversation list. * @brief MainWindow::createExistingConvo * @param data "{convo_id}|{ip}|{name}|{ip}|{name}|..." */ void MainWindow::createExistingConvo(QString data) { QStringList stuff = data.split('|', QString::SkipEmptyParts); createTab(stuff.at(0), stuff.at(1), stuff.at(2)); indicateChange(convos->count()); Conversation* newConvo = convos->at(convos->count()-1); for (int i = 3; i < stuff.count(); i++) { QString ip = stuff.at(i++); //Separated to avoid possibly undefined behavior. newConvo->add(ip, stuff.at(i)); } }
bool Manager::saveMessage(string json){ MessageFactory messageFactory; Message* message = messageFactory.createWithJsonString(json); User* sender = message->getSender(); User* receptor = message->getReceptor(); Conversation* conv = this->db->getConversation(sender ->getUsername(), receptor->getUsername() ); int messageID = conv->getNumberMessages(); message->setId(to_string(messageID)); conv->addOneMessage(messageID); this->db->saveConversation(conv); LoggerManager::getInstance()->log(LoggerManager::logInfo, "New Message created"); return this->db->saveMessage(message); }
/** * Called when tab is switched, empties the participant list, * and populates it with the participants in the newly selected * tab. * @brief MainWindow::on_tabgrpConversations_currentChanged */ void MainWindow::on_tabgrpConversations_currentChanged(int index) { ui->lstInConvo->clear(); indicateChange(index, Qt::black); if(index == 0) return; Conversation* lst = convos->at(index-1); for (int i = 0; i < lst->count() ; ++i) { Peer* p = lst->at(i); ui->lstInConvo->addItem(p->getName() + "/" + p->getIp()); } }
Message::ptr_t MessageManager::receivedMessage(Conversation &conversation, MessageData data) { auto message = make_shared<Message>(*this, data, Message::INCOMING, conversation.getId()); assert(conversation.getIdentity()); assert(conversation.getFirstParticipant()); auto cert = conversation.getFirstParticipant()->getCert(); if (!message->validate(*cert)) { LFLOG_WARN << "Incoming message from " << conversation.getFirstParticipant()->getName() << " to " << conversation.getIdentity()->getName() << " failed validation. Rejecting."; return {}; } // See if we already have received this message if (auto existing = getMessage(data.messageId, conversation.getId())) { return existing; } message->addToDb(); registry_.add(message->getId(), message); touch(message); emit messageAdded(message); return message; }
void Encounter::OnMainMenuButtonClicked(int id) { int originalId = id; // No buttons are in the list for conversations that aren't available, // so we want to bump up the ID to account for these. for (int i = 0; i <= id; i++) { Conversation *pConversation = allConversationList[i]; if (!pConversation->GetIsEnabled() || (pConversation->GetDisappearsAfterCompleted() && pConversation->GetHasBeenCompleted())) { id++; } } PrepNextConversation(allConversationList[id], originalId); }
Message::ptr_t MessageManager::sendMessage(Conversation &conversation, MessageData data) { auto message = make_shared<Message>(*this, data, Message::OUTGOING, conversation.getId()); assert(conversation.getIdentity()); auto cert = conversation.getIdentity()->getCert(); message->sign(*cert); message->addToDb(); registry_.add(message->getId(), message); touch(message); emit messageAdded(message); if (auto contact = conversation.getFirstParticipant()) { contact->queueMessage(message); } return message; }
/** * Creates a new tab, and initializes a new conversation with given ip and name. * @brief MainWindow::createTab * @param cid Conversation id. * @param ip IPv4 address * @param name Nickname */ void MainWindow::createTab(QString cid, QString ip, QString name) { // Inflate tablelayout from file QFormBuilder builder; QFile file(FORM_TAB); file.open(QFile::ReadOnly); QWidget *tabLayout = builder.load(&file, this); file.close(); // Add layout to a new tab QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(tabLayout); QWidget *newTab = new QWidget(ui->tabgrpConversations); newTab->setLayout(layout); ui->tabgrpConversations->addTab(newTab, name + "/" + ip); //Add eventlisteners to new tab button, message text-box and smiley-list. QPushButton* btnSend = tabLayout->findChild<QPushButton*>("btnTabSend"); connect(btnSend, SIGNAL(clicked()), this, SLOT(sendConvoMessage())); QTextEdit* txtMsg = tabLayout->findChild<QTextEdit*>(CONVO_TAB_MSG_ID); txtMsg->installEventFilter(this); QListWidget* lstSmil = tabLayout->findChild<QListWidget*>("lstTabSmileys"); connect(lstSmil, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_lstSmileys_doubleClicked(QModelIndex))); for (int i = 0; i < smileys->count(); i++) { QListWidgetItem* qlwi = new QListWidgetItem(QIcon(RES_SMILEYS + QString::number(i) + ".png"), ""); lstSmil->addItem(qlwi); } Conversation* c = new Conversation(cid); if(controller->isExternal(ip)) //External ip { c->add(ip, name, true); } else c->add(ip, name); convos->append(c); }
void ConversationManager::handleDatabaseJobComplete(void* ref, DatabaseResult* result) { CVAsyncContainer* asyncContainer = reinterpret_cast<CVAsyncContainer*>(ref); switch(asyncContainer->mQuery) { case ConvQuery_Conversations: { Conversation* conv; CVAsyncContainer* asCont; uint32 insertId; DataBinding* binding = mDatabase->CreateDataBinding(1); binding->addField(DFT_uint32,offsetof(Conversation,mId),4,0); uint64 count = result->getRowCount(); for(uint32 i = 0; i < count; i++) { conv = new Conversation(); result->GetNextRow(binding,conv); insertId = conv->getId(); mConversations.insert(insertId,conv); asCont = new(mDBAsyncPool.malloc()) CVAsyncContainer(ConvQuery_Pages); asCont->mConversation = conv; mDatabase->ExecuteSqlAsync(this,asCont,"SELECT * FROM conversation_pages WHERE conversation_id=%u ORDER BY page",insertId); } if(result->getRowCount()) gLogger->logMsgLoadSuccess("ConversationManager::loading %u Conversations...",MSG_NORMAL,result->getRowCount()); else gLogger->logMsgLoadFailure("ConversationManager::loading Conversations...",MSG_NORMAL); mDatabase->DestroyDataBinding(binding); } break; case ConvQuery_Pages: { ConversationPage* page; CVAsyncContainer* asCont; uint32 batchId; DataBinding* pageBinding = mDatabase->CreateDataBinding(5); pageBinding->addField(DFT_uint32,offsetof(ConversationPage,mId),4,1); pageBinding->addField(DFT_bstring,offsetof(ConversationPage,mCustomText),512,2); pageBinding->addField(DFT_bstring,offsetof(ConversationPage,mStfFile),255,3); pageBinding->addField(DFT_bstring,offsetof(ConversationPage,mStfVariable),255,4); pageBinding->addField(DFT_uint32,offsetof(ConversationPage,mAnimation),4,6); DataBinding* batchBinding = mDatabase->CreateDataBinding(1); batchBinding->addField(DFT_uint32,0,4,5); uint32 count = static_cast<uint32>(result->getRowCount()); for(uint64 i = 0; i< count; i++) { page = new ConversationPage(); result->GetNextRow(pageBinding,page); page->mCustomText.convert(BSTRType_Unicode16); result->ResetRowIndex(static_cast<int>(i)); result->GetNextRow(batchBinding,&batchId); asyncContainer->mConversation->mPages.push_back(page); // query options asCont = new(mDBAsyncPool.malloc()) CVAsyncContainer(ConvQuery_Page_OptionBatch); asCont->mConversationPage = page; mDatabase->ExecuteSqlAsync(this,asCont,"SELECT conversation_options.id,conversation_options.customText,conversation_options.stf_file," "conversation_options.stf_variable,conversation_options.event,conversation_options.pageLink " "FROM " "conversation_option_batches " "INNER JOIN conversation_options ON (conversation_option_batches.option_id = conversation_options.id) " "WHERE " "(conversation_option_batches.id = %u) ORDER BY conversation_option_batches.option_id",batchId); } mDatabase->DestroyDataBinding(pageBinding); mDatabase->DestroyDataBinding(batchBinding); } break; case ConvQuery_Page_OptionBatch: { ConversationOption* option; DataBinding* binding = mDatabase->CreateDataBinding(6); binding->addField(DFT_uint32,offsetof(ConversationOption,mId),4,0); binding->addField(DFT_bstring,offsetof(ConversationOption,mCustomText),512,1); binding->addField(DFT_bstring,offsetof(ConversationOption,mStfFile),255,2); binding->addField(DFT_bstring,offsetof(ConversationOption,mStfVariable),255,3); binding->addField(DFT_uint32,offsetof(ConversationOption,mEvent),4,4); binding->addField(DFT_uint32,offsetof(ConversationOption,mPageLinkId),4,5); uint64 count = result->getRowCount(); for(uint32 i = 0; i < count; i++) { option = new ConversationOption(); result->GetNextRow(binding,option); option->mCustomText.convert(BSTRType_Unicode16); asyncContainer->mConversationPage->mOptions.push_back(option); } mDatabase->DestroyDataBinding(binding); } break; default: break; } mDBAsyncPool.free(asyncContainer); }
void CMSN::ProcessSBPacket(const Licq::UserId& socketUserId, CMSNBuffer* packet, Licq::TCPSocket* sock) { int nSock = sock->Descriptor(); CMSNPacket *pReply; bool bSkipPacket; //while (!packet->End()) { pReply = 0; bSkipPacket = true; string strCmd = packet->unpackRawString(3); if (strCmd == "IRO") { packet->SkipParameter(); // Seq packet->SkipParameter(); // current user to add packet->SkipParameter(); // total users in conversation UserId userId(myOwnerId, packet->GetParameter()); bool newUser; { Licq::UserWriteGuard u(userId, true, &newUser); if (newUser) { // MSN uses UTF-8 so we need to set this for all new users automatically u->SetEnableSave(false); u->setUserEncoding("UTF-8"); u->SetEnableSave(true); u->save(Licq::User::SaveLicqInfo); } } // Add the user to the conversation Conversation* convo = gConvoManager.getFromSocket(nSock); if (convo == NULL) convo = gConvoManager.add(myOwnerId, nSock); convo->addUser(userId); // Notify the plugins of the new CID Licq::gPluginManager.pushPluginSignal(new Licq::PluginSignal( Licq::PluginSignal::SignalConversation, Licq::PluginSignal::ConvoCreate, userId, 0, SocketToCID(nSock))); Licq::gPluginManager.pushPluginSignal(new Licq::PluginSignal( Licq::PluginSignal::SignalConversation, Licq::PluginSignal::ConvoJoin, userId, 0, SocketToCID(nSock))); gLog.info("%s joined the conversation", userId.toString().c_str()); } else if (strCmd == "ANS") { // Send our capabilities Send_SB_Packet(Licq::UserId(), new CPS_MsnClientCaps(), sock); } else if (strCmd == "MSG") { Licq::UserId userId(myOwnerId, packet->GetParameter()); packet->SkipParameter(); // Nick string strSize = packet->GetParameter(); // Size int nSize = atoi(strSize.c_str()) + 1; // Make up for the \n unsigned long nBeforeParse = packet->getDataPosRead() - packet->getDataStart(); packet->SkipPacket(); // Skip \r\n packet->ParseHeaders(); unsigned long nAfterParse = packet->getDataPosRead() - packet->getDataStart(); int nRead = nAfterParse - nBeforeParse; nSize -= nRead; string strType = packet->GetValue("Content-Type"); if (strType == "text/x-msmsgscontrol") { packet->SkipRN(); packet->SkipRN(); packet->SkipRN(); Licq::UserWriteGuard u(userId); if (u.isLocked()) setIsTyping(*u, true, SocketToCID(nSock)); } else if (strncmp(strType.c_str(), "text/plain", 10) == 0) { gLog.info("Message from %s", userId.accountId().c_str()); bSkipPacket = false; string msg = Licq::gTranslator.returnToUnix(packet->unpackRawString(nSize)); Licq::EventMsg* e = new Licq::EventMsg(msg, time(0), 0, SocketToCID(nSock)); Licq::UserWriteGuard u(userId); if (u.isLocked()) setIsTyping(*u, false, SocketToCID(nSock)); if (Licq::gDaemon.addUserEvent(*u, e)) gOnEventManager.performOnEvent(OnEventData::OnEventMessage, *u); } else if (strncmp(strType.c_str(), "text/x-msmsgsinvite", 19) == 0) { packet->SkipRN(); packet->ParseHeaders(); string application = packet->GetValue("Application-Name"); string cookie = packet->GetValue("Invitation-Cookie"); string command = packet->GetValue("Invitation-Command"); if (command == "INVITE") { // Invitation for unknown application, tell inviter that we don't have it gLog.info("Invitation from %s for unknown application (%s)", userId.accountId().c_str(), application.c_str()); pReply = new CPS_MSNCancelInvite(cookie, "REJECT_NOT_INSTALLED"); } } else if (strncmp(strType.c_str(), "application/x-msnmsgrp2p", 24) == 0) { // Get the binary header /* unsigned long nSessionID = packet->UnpackUnsignedLong(); unsigned long nIdentifier = packet->UnpackUnsignedLong(); unsigned long nOffset[2], nSize[2], nAckDataSize[2]; nOffset[0] = packet->UnpackUnsignedLong(); nOffset[1] = packet->UnpackUnsignedLong(); nSize[0] = packet->UnpackUnsignedLong(); nSize[1] = packet->UnpackUnsignedLong(); unsigned long nLen = packet->UnpackUnsignedLong(); unsigned long nFlag = packet->UnpackUnsignedLong(); unsigned long nAckID = packet->UnpackUnsignedLong(); unsigned long nAckUniqueID = packet->UnpackUnsignedLong(); nAckDataSize[0] = packet->UnpackUnsignedLong(); nAckDataSize[1] = packet->UnpackUnsignedLong(); printf("%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\n", nSessionID, nIdentifier, nOffset[0], nOffset[1], nSize[0], nSize[1], nLen, nFlag, nAckID, nAckUniqueID, nAckDataSize[0], nAckDataSize[1]); */ CMSNDataEvent* p = FetchDataEvent(userId, sock); if (p) { if (p->ProcessPacket(packet) > 0) { RemoveDataEvent(p); } } } else if (strncmp(strType.c_str(), "text/x-clientcaps", 17) == 0) { packet->SkipRN(); packet->ParseHeaders(); string userClient = packet->GetValue("Client-Name"); if (!userClient.empty()) { gLog.info("Identified user client as %s", userClient.c_str()); Licq::UserWriteGuard u(userId); u->setClientInfo(userClient); } } else { gLog.info("Message from %s with unknown content type (%s)", userId.accountId().c_str(), strType.c_str()); } } else if (strCmd == "ACK") { string strId = packet->GetParameter(); unsigned long nSeq = (unsigned long)atoi(strId.c_str()); Licq::Event* e = RetrieveEvent(nSeq); if (e) { e->m_eResult = Licq::Event::ResultAcked; if (e->m_pUserEvent) { Conversation* convo = gConvoManager.getFromSocket(nSock); if (convo != NULL) { Licq::ConversationUsers users; convo->getUsers(users); BOOST_FOREACH(const UserId& userId, users) { Licq::UserWriteGuard u(userId); if (!u.isLocked()) continue; e->m_pUserEvent->AddToHistory(*u, false); u->SetLastSentEvent(); if (u->id() == e->userId()) gOnEventManager.performOnEvent(OnEventData::OnEventMsgSent, *u); } } else {
void Encounter::RefreshButtonArrayContents() { vector<ButtonArrayLoadParameters> loadParametersList; for (unsigned int i = 0; i < conversationList.size(); i++) { Conversation *pConversation = conversationList[i]; if (pConversation->GetIsEnabled()) { ButtonArrayLoadParameters loadParameters; loadParameters.text = pConversation->GetName(); if (pConversation->GetIsLocked()) { loadParameters.lockCount = pConversation->GetLockCount(); } loadParameters.unlockedLockCount = 0; vector<Conversation::UnlockCondition *> *pUnlockConditions = pConversation->GetUnlockConditions(); for (unsigned int i = 0; i < pUnlockConditions->size(); i++) { Conversation::UnlockCondition *pUnlockCondition = (*pUnlockConditions)[i]; if (pUnlockCondition->GetIsConditionMet() && !pUnlockCondition->GetHasHandledMetCondition()) { loadParameters.unlockedLockCount++; pUnlockCondition->SetHasHandledMetCondition(true); } } loadParameters.showCheckBox = pConversation->GetHasBeenCompleted(); loadParametersList.push_back(loadParameters); } } for (unsigned int i = 0; i < interrogationList.size(); i++) { Interrogation *pInterrogation = interrogationList[i]; if (!pInterrogation->GetHasBeenCompleted()) { if (pInterrogation->GetIsEnabled()) { ButtonArrayLoadParameters loadParameters; char text[256]; snprintf(text, 256, gpLocalizableContent->GetText("Encounter/InterrogationDesignationFormatText").c_str(), pInterrogation->GetName().c_str()); loadParameters.text = string(text); loadParametersList.push_back(loadParameters); } } } for (unsigned int i = 0; i < confrontationList.size(); i++) { Confrontation *pConfrontation = confrontationList[i]; if (!pConfrontation->GetHasBeenCompleted()) { if (pConfrontation->GetIsEnabled()) { ButtonArrayLoadParameters loadParameters; char text[256]; snprintf(text, 256, gpLocalizableContent->GetText("Encounter/ConfrontationDesignationFormatText").c_str(), pConfrontation->GetName().c_str()); loadParameters.text = string(text); loadParametersList.push_back(loadParameters); } } } pMainMenuButtonArray->Load(loadParametersList); pPresentEvidenceTab->SetIsEnabled(Case::GetInstance()->GetEvidenceManager()->GetHasEvidence()); }
int main(int argc, char *argv[]) { Conversation conversation; bool debugMode = false; for(const char *const *it = argv + 1; *it; ++it) { string arg = *it; if(arg == "-h" || arg == "--help") { PrintHelp(); return 0; } else if(arg == "-v" || arg == "--version") { PrintVersion(); return 0; } else if(arg == "-t" || arg == "--talk") conversation = LoadConversation(); else if(arg == "-d" || arg == "--debug") debugMode = true; } PlayerInfo player; try { SDL_Init(SDL_INIT_VIDEO); // Begin loading the game data. GameData::BeginLoad(argv); Audio::Init(GameData::Sources()); // On Windows, make sure that the sleep timer has at least 1 ms resolution // to avoid irregular frame rates. #ifdef _WIN32 timeBeginPeriod(1); #endif player.LoadRecent(); player.ApplyChanges(); // Check how big the window can be. SDL_DisplayMode mode; if(SDL_GetCurrentDisplayMode(0, &mode)) return DoError("Unable to query monitor resolution!"); Preferences::Load(); Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI; if(Preferences::Has("fullscreen")) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; // Make the window just slightly smaller than the monitor resolution. int maxWidth = mode.w; int maxHeight = mode.h; // Restore this after toggling fullscreen. int restoreWidth = 0; int restoreHeight = 0; if(maxWidth < 640 || maxHeight < 480) return DoError("Monitor resolution is too small!"); if(Screen::RawWidth() && Screen::RawHeight()) { // Never allow the saved screen width to be leaving less than 100 // pixels free around the window. This avoids the problem where you // maximize without going full-screen, and next time the window pops // up you can't access the resize control because it is offscreen. Screen::SetRaw( min(Screen::RawWidth(), (maxWidth - 100)), min(Screen::RawHeight(), (maxHeight - 100))); if(flags & SDL_WINDOW_FULLSCREEN_DESKTOP) { restoreWidth = Screen::RawWidth(); restoreHeight = Screen::RawHeight(); Screen::SetRaw(maxWidth, maxHeight); } } else Screen::SetRaw(maxWidth - 100, maxHeight - 100); // Make sure the zoom factor is not set too high for the full UI to fit. if(Screen::Height() < 700) Screen::SetZoom(100); // Create the window. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); #ifdef _WIN32 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); #endif SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_Window *window = SDL_CreateWindow("Endless Sky", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Screen::RawWidth(), Screen::RawHeight(), flags); if(!window) return DoError("Unable to create window!"); SDL_GLContext context = SDL_GL_CreateContext(window); if(!context) return DoError("Unable to create OpenGL context! Check if your system supports OpenGL 3.0.", window); if(SDL_GL_MakeCurrent(window, context)) return DoError("Unable to set the current OpenGL context!", window, context); SDL_GL_SetSwapInterval(1); // Initialize GLEW. #ifndef __APPLE__ glewExperimental = GL_TRUE; if(glewInit() != GLEW_OK) return DoError("Unable to initialize GLEW!", window, context); #endif // Check that the OpenGL version is high enough. const char *glVersion = reinterpret_cast<const char *>(glGetString(GL_VERSION)); if(!glVersion || !*glVersion) return DoError("Unable to query the OpenGL version!", window, context); const char *glslVersion = reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION)); if(!glslVersion || !*glslVersion) { ostringstream out; out << "Unable to query the GLSL version. OpenGL version is " << glVersion << "."; return DoError(out.str(), window, context); } if(*glVersion < '3') { ostringstream out; out << "Endless Sky requires OpenGL version 3.0 or higher." << endl; out << "Your OpenGL version is " << glVersion << ", GLSL version " << glslVersion << "." << endl; out << "Please update your graphics drivers."; return DoError(out.str(), window, context); } glClearColor(0.f, 0.f, 0.0f, 1.f); glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); GameData::LoadShaders(); { // Check whether this is a high-DPI window. int width = 0; int height = 0; SDL_GL_GetDrawableSize(window, &width, &height); Screen::SetHighDPI(width > Screen::RawWidth() && height > Screen::RawHeight()); // Fix a possible race condition leading to the wrong window dimensions. glViewport(0, 0, width, height); } UI gamePanels; UI menuPanels; menuPanels.Push(new MenuPanel(player, gamePanels)); if(!conversation.IsEmpty()) menuPanels.Push(new ConversationPanel(player, conversation)); string swizzleName = "_texture_swizzle"; #ifndef __APPLE__ const char *extensions = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)); if(!strstr(extensions, swizzleName.c_str())) #else bool hasSwizzle = false; GLint extensionCount; glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount); for(GLint i = 0; i < extensionCount && !hasSwizzle; ++i) { const char *extension = reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i)); hasSwizzle = (extension && strstr(extension, swizzleName.c_str())); } if(!hasSwizzle) #endif menuPanels.Push(new Dialog( "Note: your computer does not support the \"texture swizzling\" OpenGL feature, " "which Endless Sky uses to draw ships in different colors depending on which " "government they belong to. So, all human ships will be the same color, which " "may be confusing. Consider upgrading your graphics driver (or your OS).")); FrameTimer timer(60); bool isPaused = false; while(!menuPanels.IsDone()) { // Handle any events that occurred in this frame. SDL_Event event; while(SDL_PollEvent(&event)) { UI &activeUI = (menuPanels.IsEmpty() ? gamePanels : menuPanels); // The caps lock key slows the game down (to make it easier to // see and debug things that are happening quickly). if(debugMode && (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && event.key.keysym.sym == SDLK_CAPSLOCK) { timer.SetFrameRate((event.key.keysym.mod & KMOD_CAPS) ? 10 : 60); } else if(debugMode && event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_BACKQUOTE) { isPaused = !isPaused; } else if(event.type == SDL_KEYDOWN && menuPanels.IsEmpty() && Command(event.key.keysym.sym).Has(Command::MENU) && !gamePanels.IsEmpty() && gamePanels.Top()->IsInterruptible()) { menuPanels.Push(shared_ptr<Panel>( new MenuPanel(player, gamePanels))); } else if(event.type == SDL_QUIT) { menuPanels.Quit(); } else if(event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { int width = event.window.data1 & ~1; int height = event.window.data2 & ~1; if(width != Screen::RawWidth() || height != Screen::RawHeight()) { Screen::SetRaw(width, height); if((event.window.data1 | event.window.data2) & 1) SDL_SetWindowSize(window, Screen::RawWidth(), Screen::RawHeight()); SDL_GL_GetDrawableSize(window, &width, &height); glViewport(0, 0, width, height); } } else if(event.type == SDL_KEYDOWN && (Command(event.key.keysym.sym).Has(Command::FULLSCREEN) || (event.key.keysym.sym == SDLK_RETURN && event.key.keysym.mod & KMOD_ALT))) { if(restoreWidth) { SDL_SetWindowFullscreen(window, 0); Screen::SetRaw(restoreWidth, restoreHeight); SDL_SetWindowSize(window, Screen::RawWidth(), Screen::RawHeight()); restoreWidth = 0; restoreHeight = 0; } else { restoreWidth = Screen::RawWidth(); restoreHeight = Screen::RawHeight(); Screen::SetRaw(maxWidth, maxHeight); SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); } int width, height; SDL_GL_GetDrawableSize(window, &width, &height); glViewport(0, 0, width, height); } else if(activeUI.Handle(event)) { // No need to do anything more! } } Font::ShowUnderlines(SDL_GetModState() & KMOD_ALT); // Tell all the panels to step forward, then draw them. ((!isPaused && menuPanels.IsEmpty()) ? gamePanels : menuPanels).StepAll(); Audio::Step(); // That may have cleared out the menu, in which case we should draw // the game panels instead: (menuPanels.IsEmpty() ? gamePanels : menuPanels).DrawAll(); SDL_GL_SwapWindow(window); timer.Wait(); } // If you quit while landed on a planet, save the game. if(player.GetPlanet()) player.Save(); // The Preferences class reads the screen dimensions, so update them if // the window is full screen: bool isFullscreen = (restoreWidth != 0); Preferences::Set("fullscreen", isFullscreen); if(isFullscreen) Screen::SetRaw(restoreWidth, restoreHeight); Preferences::Save(); Cleanup(window, context); } catch(const runtime_error &error) { DoError(error.what()); } return 0; }