first commit

This commit is contained in:
sanya
2025-09-01 14:20:39 +00:00
committed by ExternPointer
commit 490fc11f6a
4328 changed files with 1796224 additions and 0 deletions

View File

@@ -0,0 +1 @@
build-*

View File

@@ -0,0 +1,290 @@
![Demo shot](https://cldup.com/98tHyoJJE7.gif)
In this tutorial well learn how to create a QT chat application that communicates with a [Socket.IO Node.JS chat server](https://github.com/Automattic/socket.io/tree/master/examples/chat).
### Introduction
To follow along, start by cloning the repository: [socket.io-client-cpp](https://github.com/socketio/socket.io-client-cpp).
Using:
```bash
git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git
```
The app has the following features:
* Sending a message to all users joining to the room.
* Notifies when each user joins or leaves.
* Notifies when an user start typing a message.
###Install QT community
Visit [QT community download link](http://www.qt.io/download-open-source/#section-2) to get the install package.
Just install it with default installation option.
###Create a QT GUI application.
Launch QT Creator.
In welcome page, select `New Project`, create a `QT Widget Application`, named it `SioChatDemo`
The project structure is like:
```
SioChatDemo
|__ SioChatDemo.pro
|__Headers
| |__mainwindow.h
|__Sources
| |__main.cpp
| |__mainwindow.cpp
|__Forms
|__mainwindow.ui
```
### Import SioClient and config compile options.
Let's copy the SioClient into the QT project as a subfolder `sioclient`.
Edit `SioChatDemo.pro` to config paths and compile options, simply add:
```bash
SOURCES += ./sioclient/src/sio_client.cpp \
./sioclient/src/sio_packet.cpp
HEADERS += ./sioclient/src/sio_client.h \
./sioclient/src/sio_message.h
INCLUDEPATH += $$PWD/sioclient/lib/rapidjson/include
INCLUDEPATH += $$PWD/sioclient/lib/websocketpp
```
Also add two additional compile option
```bash
CONFIG+=no_keywords
CONFIG+=c++11
```
`no_keywords` is for preventing qmake treat some function's name `emit` as the keyword of signal-slot mechanism.
`c++11` ask for C++11 support.
### Make up mainwindow ui.
Make up a simple ui by drag and drop widget from `Widget box` in left side.
We finally end up with this:
![QT mainwindow](https://cldup.com/RI98CYpYL5.png)
It contains:
* a `QLineEdit` at the top for nickname inputing, named `nickNameEdit`
* a `QPushButton` at the topright for login, named `loginBtn`
* a `QListWidget` at the center for showing messages, named `listView`
* a `QLineEdit` at the bottom for typing message, named `messageEdit`
* a `QPushButton` at the bottomright for sending message, named `sendBtn`
### Add Slots in mainwindow
Slots need to be added in `mainwindow` class to handle UI events.They are
* click login button
* click send message button
* text change in messageEdit(for typing status)
* message editing is returned (for sending message by return)
Insert following code into `MainWindow` class in `mainwindow.h`
```C++
public Q_SLOTS:
void SendBtnClicked();
void TypingChanged();
void LoginClicked();
void OnMessageReturn();
```
### Connect UI event signal and slots together
Open `mainwindow.ui` in Design mode. switch to `signals/slots` mode by check `Menu->Edit->Edit Signals/Slots`
By press left mouse on widget and drag on to the window (cursor will become a sign of electrical ground), to open the connection editor.
In the connection editor, edit the slots of MainWindow at the right side, add Those slots function name added in `mainwindow.h` before.
Then we'll be able to connect the event signal from widget with our own slots.
We finally end up with this:
![QT signals&slots](https://cldup.com/Vsb-UXG3FC.jpg)
### Adding UI refresh Signals/Slots
`sio::client`'s callbacks are not in UI thread. However, UI is required to be updated by those callbacks, so we need some `Signal` for non-UI thread to "request" `Slots` functions been called in UI thread. Say if we want to signal `QListWidgetItem` being added, add:
```C++
//In mainwindow.h
Q_SIGNALS:
void RequestAddListItem(QListWidgetItem *item);
private Q_SLOTS:
void AddListItem(QListWidgetItem *item);
```
```C++
//In mainwindow.cpp
void MainWindow::AddListItem(QListWidgetItem* item)
{
this->findChild<QListWidget*>("listView")->addItem(item);
}
```
Then connect them in `MainWindow` constructor.
```C++
connect(this,SIGNAL(RequestAddListItem(QListWidgetItem*)),this,SLOT(AddListItem(QListWidgetItem*)));
```
### Init sio::client in MainWindow
For single window applications, simply let `MainWindow` class holding the `sio::client` object:
declare a `unique_ptr` member of `sio::client` and Several event handling functions in `mainwindow.h`
```C++
private:
void OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnUserJoined(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnUserLeft(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnTyping(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnStopTyping(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnLogin(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp);
void OnConnected();
void OnClosed(client::close_reason const& reason);
void OnFailed();
std::unique_ptr<client> _io;
```
Init `sio::client` and setup event bindings for default `socket` in `MainWindow` constructor.
And we also need to handle the connectivity events, handle the connect and disconnect events.
Now the `MainWindow` constructor:
```C++
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
_io(new client())
{
ui->setupUi(this);
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
socket::ptr sock = _io->socket();
sock->on("new message",std::bind(&MainWindow::OnNewMessage,this,_1,_2,_3,_4));
sock->on("user joined",std::bind(&MainWindow::OnUserJoined,this,_1,_2,_3,_4));
sock->on("user left",std::bind(&MainWindow::OnUserLeft,this,_1,_2,_3,_4));
sock->on("typing",std::bind(&MainWindow::OnTyping,this,_1,_2,_3,_4));
sock->on("stop typing",std::bind(&MainWindow::OnStopTyping,this,_1,_2,_3,_4));
sock->on("login",std::bind(&MainWindow::OnLogin,this,_1,_2,_3,_4));
//default socket opened, also we have "set_open_listener" for monitoring physical connection opened.
_io->set_socket_open_listener(std::bind(&MainWindow::OnConnected,this,std::placeholders::_1));
//physical connection closed or drop.
_io->set_close_listener(std::bind(&MainWindow::OnClosed,this,_1));
//physical connection fail to establish.
_io->set_fail_listener(std::bind(&MainWindow::OnFailed,this));
connect(this,SIGNAL(RequestAddListItem(QListWidgetItem*)),this,SLOT(AddListItem(QListWidgetItem*)));
}
```
### Managing connection state
We have several connection listeners for connection events.
First we want to send login message once we're connected, get the default `socket` from `client` to do that.
```C++
void MainWindow::OnConnected()
{
QByteArray bytes = m_name.toUtf8();
std::string nickName(bytes.data(),bytes.length());
_io->socket()->emit("add user", nickName);
}
```
Then if connection is closed or failed, we need to restore to the UI before connect.
```C++
void MainWindow::OnClosed(client::close_reason const& reason)
{
//restore UI to pre-login state
}
void MainWindow::OnFailed()
{
//restore UI to pre-login state
}
```
If `MainWindow` is exit, we need to clear event bindings and listeners.
the `sio::client` object will be destruct by `unique_ptr`
```C++
MainWindow::~MainWindow()
{
_io->socket()->off_all();
_io->socket()->off_error();
delete ui;
}
```
### Handle socket.io events
We'll need to handle socket.io events in our functions bind to socket.io events.
For example, we need to show received messages to the `listView`
```C++
void MainWindow::OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::ptr &ack_resp)
{
if(data->get_flag() == message::flag_object)
{
std::string msg = data->get_map()["message"]->get_string();
std::string name = data->get_map()["username"]->get_string();
QString label = QString::fromUtf8(name.data(),name.length());
label.append(':');
label.append(QString::fromUtf8(msg.data(),msg.length()));
QListWidgetItem *item= new QListWidgetItem(label);
//emit RequestAddListItem signal
//so that 'AddListItem' will be executed in UI thread.
Q_EMIT RequestAddListItem(item);
}
}
```
### Sending chat message
When `sendBtn` is clicked, we need to send the text in `messageEdit` to chatroom.
Add code to `SendBtnClicked()`:
```C++
void MainWindow::SendBtnClicked()
{
QLineEdit* messageEdit = this->findChild<QLineEdit*>("messageEdit");
QString text = messageEdit->text();
if(text.length()>0)
{
QByteArray bytes = text.toUtf8();
std::string msg(bytes.data(),bytes.length());
_io->socket()->emit("new message",msg);//emit new message
text.append(":You");
QListWidgetItem *item = new QListWidgetItem(text);
item->setTextAlignment(Qt::AlignRight);
Q_EMIT RequestAddListItem(item);
messageEdit->clear();
}
}
```
### Further reading
You can run [Demo project](https://github.com/socketio/socket.io-client-cpp/tree/master/examples/QT/SioChatDemo) to have a closer look.
Before running, please follow the [instructions](../../README.md#with_cmake) to make the sioclient library.

View File

@@ -0,0 +1 @@
SioChatDemo.pro.user

View File

@@ -0,0 +1,44 @@
#-------------------------------------------------
#
# Project created by QtCreator 2015-03-30T19:25:23
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = SioChatDemo
TEMPLATE = app
CONFIG+=no_keywords
CONFIG+=c++11
SOURCES += main.cpp\
mainwindow.cpp \
nicknamedialog.cpp
HEADERS += mainwindow.h \
nicknamedialog.h
FORMS += mainwindow.ui \
nicknamedialog.ui
CONFIG(debug, debug|release):DEFINES +=DEBUG=1
INCLUDEPATH += $$PWD/../../../build/include
DEPENDPATH += $$PWD/../../../build/lib
CONFIG(release, debug|release): LIBS += -L$$PWD/../../../build/lib/Release/ -lsioclient
else:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../build/lib/Debug/ -lsioclient
CONFIG(release, debug|release): LIBS += -L$$PWD/../../../build/lib/Release/ -lboost_random
else:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../build/lib/Debug/ -lboost_random
CONFIG(release, debug|release): LIBS += -L$$PWD/../../../build/lib/Release/ -lboost_system
else:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../build/lib/Debug/ -lboost_system
CONFIG(release, debug|release): LIBS += -L$$PWD/../../../build/lib/Release/ -lboost_date_time
else:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../build/lib/Debug/ -lboost_date_time

View File

@@ -0,0 +1,4 @@
osx/*
src/*
src
osx

View File

@@ -0,0 +1,378 @@
#===============================================================================
# Filename: boost.sh
# Author: Pete Goodliffe
# Copyright: (c) Copyright 2009 Pete Goodliffe
# Licence: Please feel free to use this, with attribution
# Modified version
#===============================================================================
#
# Builds a Boost framework for the iPhone.
# Creates a set of universal libraries that can be used on an iPhone and in the
# iPhone simulator. Then creates a pseudo-framework to make using boost in Xcode
# less painful.
#
# To configure the script, define:
# BOOST_LIBS: which libraries to build
# IPHONE_SDKVERSION: iPhone SDK version (e.g. 5.1)
#
# Then go get the source tar.bz of the boost you want to build, shove it in the
# same directory as this script, and run "./boost.sh". Grab a cuppa. And voila.
#===============================================================================
: ${BOOST_LIBS:="random regex graph random chrono thread signals filesystem system date_time"}
: ${IPHONE_SDKVERSION:=`xcodebuild -showsdks | grep iphoneos | egrep "[[:digit:]]+\.[[:digit:]]+" -o | tail -1`}
: ${OSX_SDKVERSION:=10.8}
: ${XCODE_ROOT:=`xcode-select -print-path`}
: ${EXTRA_CPPFLAGS:="-DBOOST_AC_USE_PTHREADS -DBOOST_SP_USE_PTHREADS -std=c++11 -stdlib=libc++"}
# The EXTRA_CPPFLAGS definition works around a thread race issue in
# shared_ptr. I encountered this historically and have not verified that
# the fix is no longer required. Without using the posix thread primitives
# an invalid compare-and-swap ARM instruction (non-thread-safe) was used for the
# shared_ptr use count causing nasty and subtle bugs.
#
# Should perhaps also consider/use instead: -BOOST_SP_USE_PTHREADS
: ${TARBALLDIR:=`pwd`}
: ${SRCDIR:=`pwd`/src}
: ${IOSBUILDDIR:=`pwd`/ios/build}
: ${OSXBUILDDIR:=`pwd`/osx/build}
: ${PREFIXDIR:=`pwd`/ios/prefix}
: ${IOSFRAMEWORKDIR:=`pwd`/ios/framework}
: ${OSXFRAMEWORKDIR:=`pwd`/osx/framework}
: ${COMPILER:="clang++"}
: ${BOOST_VERSION:=1.55.0}
: ${BOOST_VERSION2:=1_55_0}
BOOST_TARBALL=$TARBALLDIR/boost_$BOOST_VERSION2.tar.bz2
BOOST_SRC=$SRCDIR/boost_${BOOST_VERSION2}
#===============================================================================
ARM_DEV_CMD="xcrun --sdk iphoneos"
SIM_DEV_CMD="xcrun --sdk iphonesimulator"
OSX_DEV_CMD="xcrun --sdk macosx"
ARM_COMBINED_LIB=$IOSBUILDDIR/lib_boost_arm.a
SIM_COMBINED_LIB=$IOSBUILDDIR/lib_boost_x86.a
#===============================================================================
#===============================================================================
# Functions
#===============================================================================
abort()
{
echo
echo "Aborted: $@"
exit 1
}
doneSection()
{
echo
echo "================================================================="
echo "Done"
echo
}
#===============================================================================
cleanEverythingReadyToStart()
{
echo Cleaning everything before we start to build...
rm -rf iphone-build iphonesim-build osx-build
rm -rf $IOSBUILDDIR
rm -rf $OSXBUILDDIR
rm -rf $PREFIXDIR
rm -rf $IOSFRAMEWORKDIR/$FRAMEWORK_NAME.framework
rm -rf $OSXFRAMEWORKDIR/$FRAMEWORK_NAME.framework
doneSection
}
#===============================================================================
downloadBoost()
{
if [ ! -s $TARBALLDIR/boost_${BOOST_VERSION2}.tar.bz2 ]; then
echo "Downloading boost ${BOOST_VERSION}"
curl -L -o $TARBALLDIR/boost_${BOOST_VERSION2}.tar.bz2 http://sourceforge.net/projects/boost/files/boost/${BOOST_VERSION}/boost_${BOOST_VERSION2}.tar.bz2/download
fi
doneSection
}
#===============================================================================
unpackBoost()
{
[ -f "$BOOST_TARBALL" ] || abort "Source tarball missing."
echo Unpacking boost into $SRCDIR...
[ -d $SRCDIR ] || mkdir -p $SRCDIR
[ -d $BOOST_SRC ] || ( cd $SRCDIR; tar xfj $BOOST_TARBALL )
[ -d $BOOST_SRC ] && echo " ...unpacked as $BOOST_SRC"
doneSection
}
#===============================================================================
restoreBoost()
{
cp $BOOST_SRC/tools/build/v2/user-config.jam-bk $BOOST_SRC/tools/build/v2/user-config.jam
}
#===============================================================================
updateBoost()
{
echo Updating boost into $BOOST_SRC...
cp $BOOST_SRC/tools/build/v2/user-config.jam $BOOST_SRC/tools/build/v2/user-config.jam-bk
cat >> $BOOST_SRC/tools/build/v2/user-config.jam <<EOF
using darwin : ${IPHONE_SDKVERSION}~iphone
: $XCODE_ROOT/Toolchains/XcodeDefault.xctoolchain/usr/bin/$COMPILER -arch armv6 -arch armv7 -arch armv7s -arch arm64 -fvisibility=hidden -fvisibility-inlines-hidden $EXTRA_CPPFLAGS
: <striper> <root>$XCODE_ROOT/Platforms/iPhoneOS.platform/Developer
: <architecture>arm <target-os>iphone
;
using darwin : ${IPHONE_SDKVERSION}~iphonesim
: $XCODE_ROOT/Toolchains/XcodeDefault.xctoolchain/usr/bin/$COMPILER -arch i386 -arch x86_64 -fvisibility=hidden -fvisibility-inlines-hidden $EXTRA_CPPFLAGS
: <striper> <root>$XCODE_ROOT/Platforms/iPhoneSimulator.platform/Developer
: <architecture>x86 <target-os>iphone
;
EOF
doneSection
}
#===============================================================================
inventMissingHeaders()
{
# These files are missing in the ARM iPhoneOS SDK, but they are in the simulator.
# They are supported on the device, so we copy them from x86 SDK to a staging area
# to use them on ARM, too.
echo Invent missing headers
cp $XCODE_ROOT/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${IPHONE_SDKVERSION}.sdk/usr/include/{crt_externs,bzlib}.h $BOOST_SRC
}
#===============================================================================
bootstrapBoost()
{
cd $BOOST_SRC
BOOST_LIBS_COMMA=$(echo $BOOST_LIBS | sed -e "s/ /,/g")
echo "Bootstrapping (with libs $BOOST_LIBS_COMMA)"
./bootstrap.sh --with-libraries=$BOOST_LIBS_COMMA
doneSection
}
#===============================================================================
buildBoostForIPhoneOS()
{
cd $BOOST_SRC
# Install this one so we can copy the includes for the frameworks...
./bjam -j16 --build-dir=iphone-build --stagedir=iphone-build/stage --prefix=$PREFIXDIR toolset=darwin architecture=arm target-os=iphone macosx-version=iphone-${IPHONE_SDKVERSION} define=_LITTLE_ENDIAN link=static stage
./bjam -j16 --build-dir=iphone-build --stagedir=iphone-build/stage --prefix=$PREFIXDIR toolset=darwin architecture=arm target-os=iphone macosx-version=iphone-${IPHONE_SDKVERSION} define=_LITTLE_ENDIAN link=static install
doneSection
./bjam -j16 --build-dir=iphonesim-build --stagedir=iphonesim-build/stage --toolset=darwin-${IPHONE_SDKVERSION}~iphonesim architecture=x86 target-os=iphone macosx-version=iphonesim-${IPHONE_SDKVERSION} link=static stage
doneSection
# ./b2 -j16 --build-dir=osx-build --stagedir=osx-build/stage toolset=clang cxxflags="-std=c++11 -stdlib=libc++ -arch i386 -arch x86_64" linkflags="-stdlib=libc++" link=static threading=multi stage
doneSection
}
#===============================================================================
scrunchAllLibsTogetherInOneLibPerPlatform()
{
cd $BOOST_SRC
mkdir -p $IOSBUILDDIR/armv6/obj
mkdir -p $IOSBUILDDIR/armv7/obj
mkdir -p $IOSBUILDDIR/armv7s/obj
mkdir -p $IOSBUILDDIR/arm64/obj
mkdir -p $IOSBUILDDIR/i386/obj
mkdir -p $IOSBUILDDIR/x86_64/obj
mkdir -p $OSXBUILDDIR/i386/obj
mkdir -p $OSXBUILDDIR/x86_64/obj
ALL_LIBS=""
echo Splitting all existing fat binaries...
for NAME in $BOOST_LIBS; do
ALL_LIBS="$ALL_LIBS libboost_$NAME.a"
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin armv6 -o $IOSBUILDDIR/armv6/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin armv7 -o $IOSBUILDDIR/armv7/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin armv7s -o $IOSBUILDDIR/armv7s/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphone-build/stage/lib/libboost_$NAME.a" -thin arm64 -o $IOSBUILDDIR/arm64/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphonesim-build/stage/lib/libboost_$NAME.a" -thin i386 -o $IOSBUILDDIR/i386/libboost_$NAME.a
$ARM_DEV_CMD lipo "iphonesim-build/stage/lib/libboost_$NAME.a" -thin x86_64 -o $IOSBUILDDIR/x86_64/libboost_$NAME.a
$ARM_DEV_CMD lipo "osx-build/stage/lib/libboost_$NAME.a" -thin i386 -o $OSXBUILDDIR/i386/libboost_$NAME.a
$ARM_DEV_CMD lipo "osx-build/stage/lib/libboost_$NAME.a" -thin x86_64 -o $OSXBUILDDIR/x86_64/libboost_$NAME.a
done
echo "Decomposing each architecture's .a files"
for NAME in $ALL_LIBS; do
echo Decomposing $NAME...
(cd $IOSBUILDDIR/armv6/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/armv7/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/armv7s/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/arm64/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/i386/obj; ar -x ../$NAME );
(cd $IOSBUILDDIR/x86_64/obj; ar -x ../$NAME );
(cd $OSXBUILDDIR/i386/obj; ar -x ../$NAME );
(cd $OSXBUILDDIR/x86_64/obj; ar -x ../$NAME );
done
echo "Linking each architecture into an uberlib ($ALL_LIBS => libboost.a )"
rm $IOSBUILDDIR/*/libboost.a
echo ...armv6
(cd $IOSBUILDDIR/armv6; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...armv7
(cd $IOSBUILDDIR/armv7; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...armv7s
(cd $IOSBUILDDIR/armv7s; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...arm64
(cd $IOSBUILDDIR/arm64; $ARM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...i386
(cd $IOSBUILDDIR/i386; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...x86_64
(cd $IOSBUILDDIR/x86_64; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
rm $OSXBUILDDIR/*/libboost.a
echo ...osx-i386
(cd $OSXBUILDDIR/i386; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
echo ...x86_64
(cd $OSXBUILDDIR/x86_64; $SIM_DEV_CMD ar crus libboost.a obj/*.o; )
}
#===============================================================================
buildFramework()
{
: ${1:?}
FRAMEWORKDIR=$1
BUILDDIR=$2
VERSION_TYPE=Alpha
FRAMEWORK_NAME=boost
FRAMEWORK_VERSION=A
FRAMEWORK_CURRENT_VERSION=$BOOST_VERSION
FRAMEWORK_COMPATIBILITY_VERSION=$BOOST_VERSION
FRAMEWORK_BUNDLE=$FRAMEWORKDIR/$FRAMEWORK_NAME.framework
echo "Framework: Building $FRAMEWORK_BUNDLE from $BUILDDIR..."
rm -rf $FRAMEWORK_BUNDLE
echo "Framework: Setting up directories..."
mkdir -p $FRAMEWORK_BUNDLE
mkdir -p $FRAMEWORK_BUNDLE/Versions
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Resources
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Headers
mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Documentation
echo "Framework: Creating symlinks..."
ln -s $FRAMEWORK_VERSION $FRAMEWORK_BUNDLE/Versions/Current
ln -s Versions/Current/Headers $FRAMEWORK_BUNDLE/Headers
ln -s Versions/Current/Resources $FRAMEWORK_BUNDLE/Resources
ln -s Versions/Current/Documentation $FRAMEWORK_BUNDLE/Documentation
ln -s Versions/Current/$FRAMEWORK_NAME $FRAMEWORK_BUNDLE/$FRAMEWORK_NAME
FRAMEWORK_INSTALL_NAME=$FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/$FRAMEWORK_NAME
echo "Lipoing library into $FRAMEWORK_INSTALL_NAME..."
$ARM_DEV_CMD lipo -create $BUILDDIR/*/libboost.a -o "$FRAMEWORK_INSTALL_NAME" || abort "Lipo $1 failed"
echo "Framework: Copying includes..."
cp -r $PREFIXDIR/include/boost/* $FRAMEWORK_BUNDLE/Headers/
echo "Framework: Creating plist..."
cat > $FRAMEWORK_BUNDLE/Resources/Info.plist <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${FRAMEWORK_NAME}</string>
<key>CFBundleIdentifier</key>
<string>org.boost</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${FRAMEWORK_CURRENT_VERSION}</string>
</dict>
</plist>
EOF
doneSection
}
#===============================================================================
# Execution starts here
#===============================================================================
mkdir -p $IOSBUILDDIR
cleanEverythingReadyToStart #may want to comment if repeatedly running during dev
restoreBoost
echo "BOOST_VERSION: $BOOST_VERSION"
echo "BOOST_LIBS: $BOOST_LIBS"
echo "BOOST_SRC: $BOOST_SRC"
echo "IOSBUILDDIR: $IOSBUILDDIR"
echo "OSXBUILDDIR: $OSXBUILDDIR"
echo "PREFIXDIR: $PREFIXDIR"
echo "IOSFRAMEWORKDIR: $IOSFRAMEWORKDIR"
echo "OSXFRAMEWORKDIR: $OSXFRAMEWORKDIR"
echo "IPHONE_SDKVERSION: $IPHONE_SDKVERSION"
echo "XCODE_ROOT: $XCODE_ROOT"
echo "COMPILER: $COMPILER"
echo
downloadBoost
unpackBoost
inventMissingHeaders
bootstrapBoost
updateBoost
buildBoostForIPhoneOS
scrunchAllLibsTogetherInOneLibPerPlatform
buildFramework $IOSFRAMEWORKDIR $IOSBUILDDIR
buildFramework $OSXFRAMEWORKDIR $OSXBUILDDIR
restoreBoost
echo "Completed successfully"
#===============================================================================

View File

@@ -0,0 +1,11 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

View File

@@ -0,0 +1,291 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <functional>
#include <mutex>
#include <cstdlib>
#define kURL "ws://localhost:3000"
#ifdef WIN32
#define BIND_EVENT(IO,EV,FN) \
do{ \
socket::event_listener_aux l = FN;\
IO->on(EV,l);\
} while(0)
#else
#define BIND_EVENT(IO,EV,FN) \
IO->on(EV,FN)
#endif
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
_io(new client()),
m_typingItem(NULL),
m_dialog()
{
ui->setupUi(this);
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
socket::ptr sock = _io->socket();
BIND_EVENT(sock,"new message",std::bind(&MainWindow::OnNewMessage,this,_1,_2,_3,_4));
BIND_EVENT(sock,"user joined",std::bind(&MainWindow::OnUserJoined,this,_1,_2,_3,_4));
BIND_EVENT(sock,"user left",std::bind(&MainWindow::OnUserLeft,this,_1,_2,_3,_4));
BIND_EVENT(sock,"typing",std::bind(&MainWindow::OnTyping,this,_1,_2,_3,_4));
BIND_EVENT(sock,"stop typing",std::bind(&MainWindow::OnStopTyping,this,_1,_2,_3,_4));
BIND_EVENT(sock,"login",std::bind(&MainWindow::OnLogin,this,_1,_2,_3,_4));
_io->set_socket_open_listener(std::bind(&MainWindow::OnConnected,this,std::placeholders::_1));
_io->set_close_listener(std::bind(&MainWindow::OnClosed,this,_1));
_io->set_fail_listener(std::bind(&MainWindow::OnFailed,this));
connect(this,SIGNAL(RequestAddListItem(QListWidgetItem*)),this,SLOT(AddListItem(QListWidgetItem*)));
connect(this,SIGNAL(RequestRemoveListItem(QListWidgetItem*)),this,SLOT(RemoveListItem(QListWidgetItem*)));
connect(this,SIGNAL(RequestToggleInputs(bool)),this,SLOT(ToggleInputs(bool)));
}
MainWindow::~MainWindow()
{
_io->socket()->off_all();
_io->socket()->off_error();
delete ui;
}
void MainWindow::SendBtnClicked()
{
QLineEdit* messageEdit = this->findChild<QLineEdit*>("messageEdit");
QString text = messageEdit->text();
if(text.length()>0)
{
QByteArray bytes = text.toUtf8();
std::string msg(bytes.data(),bytes.length());
_io->socket()->emit("new message",msg);
text.append(" : You");
QListWidgetItem *item = new QListWidgetItem(text);
item->setTextAlignment(Qt::AlignRight);
Q_EMIT RequestAddListItem(item);
messageEdit->clear();
}
}
void MainWindow::OnMessageReturn()
{
this->SendBtnClicked();
}
void MainWindow::ShowLoginDialog()
{
m_dialog.reset(new NicknameDialog(this));
connect(m_dialog.get(),SIGNAL(accepted()),this,SLOT(NicknameAccept()));
connect(m_dialog.get(),SIGNAL(rejected()),this,SLOT(NicknameCancelled()));
m_dialog->exec();
}
void MainWindow::showEvent(QShowEvent *event)
{
ShowLoginDialog();
}
void MainWindow::TypingStop()
{
m_timer.reset();
_io->socket()->emit("stop typing");
}
void MainWindow::TypingChanged()
{
if(m_timer&&m_timer->isActive())
{
m_timer->stop();
}
else
{
_io->socket()->emit("typing");
}
m_timer.reset(new QTimer(this));
connect(m_timer.get(),SIGNAL(timeout()),this,SLOT(TypingStop()));
m_timer->setSingleShot(true);
m_timer->start(1000);
}
void MainWindow::NicknameAccept()
{
m_name = m_dialog->getNickname();
if(m_name.length()>0)
{
_io->connect(kURL);
}
}
void MainWindow::NicknameCancelled()
{
QApplication::exit();
}
void MainWindow::AddListItem(QListWidgetItem* item)
{
this->findChild<QListWidget*>("listView")->addItem(item);
}
void MainWindow::RemoveListItem(QListWidgetItem* item)
{
QListWidget* list = this->findChild<QListWidget*>("listView");
int row = list->row(item);
delete list->takeItem(row);
}
void MainWindow::OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
if(data->get_flag() == message::flag_object)
{
std::string msg = data->get_map()["message"]->get_string();
std::string username = data->get_map()["username"]->get_string();
QString label = QString::fromUtf8(username.data(),username.length());
label.append(" : ");
label.append(QString::fromUtf8(msg.data(),msg.length()));
QListWidgetItem *item= new QListWidgetItem(label);
Q_EMIT RequestAddListItem(item);
}
}
void MainWindow::OnUserJoined(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
if(data->get_flag() == message::flag_object)
{
std::string name = data->get_map()["username"]->get_string();
int numUser = data->get_map()["numUsers"]->get_int();
QString label = QString::fromUtf8(name.data(),name.length());
bool plural = numUser != 1;
label.append(" joined\n");
label.append(plural?"there are ":"there's ");
QString digits;
while(numUser>=10)
{
digits.insert(0,QChar((numUser%10)+'0'));
numUser/=10;
}
digits.insert(0,QChar(numUser+'0'));
label.append(digits);
label.append(plural?" participants":" participant");
QListWidgetItem *item= new QListWidgetItem(label);
item->setTextAlignment(Qt::AlignHCenter);
QFont font;
font.setPointSize(9);
item->setFont(font);
Q_EMIT RequestAddListItem(item);
}
}
void MainWindow::OnUserLeft(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
if(data->get_flag() == message::flag_object)
{
std::string name = data->get_map()["username"]->get_string();
int numUser = data->get_map()["numUsers"]->get_int();
QString label = QString::fromUtf8(name.data(),name.length());
bool plural = numUser != 1;
label.append(" left\n");
label.append(plural?"there are ":"there's ");
QString digits;
while(numUser>=10)
{
digits.insert(0,QChar((numUser%10)+'0'));
numUser/=10;
}
digits.insert(0,QChar(numUser+'0'));
label.append(digits);
label.append(plural?" participants":" participant");
QListWidgetItem *item= new QListWidgetItem(label);
item->setTextAlignment(Qt::AlignHCenter);
QFont font;
font.setPointSize(9);
item->setFont(font);
Q_EMIT RequestAddListItem(item);
}
}
void MainWindow::OnTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
if(m_typingItem == NULL)
{
std::string name = data->get_map()["username"]->get_string();
QString label = QString::fromUtf8(name.data(),name.length());
label.append(" is typing...");
QListWidgetItem *item = new QListWidgetItem(label);
item->setTextColor(QColor(200,200,200,255));
m_typingItem = item;
Q_EMIT RequestAddListItem(item);
}
}
void MainWindow::OnStopTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
if(m_typingItem != NULL)
{
Q_EMIT RequestRemoveListItem(m_typingItem);
m_typingItem = NULL;
}
}
void MainWindow::OnLogin(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp)
{
Q_EMIT RequestToggleInputs(true);
int numUser = data->get_map()["numUsers"]->get_int();
QString digits;
bool plural = numUser !=1;
while(numUser>=10)
{
digits.insert(0,QChar((numUser%10)+'0'));
numUser/=10;
}
digits.insert(0,QChar(numUser+'0'));
digits.insert(0,plural?"there are ":"there's ");
digits.append(plural? " participants":" participant");
QListWidgetItem *item = new QListWidgetItem(digits);
item->setTextAlignment(Qt::AlignHCenter);
QFont font;
font.setPointSize(9);
item->setFont(font);
Q_EMIT RequestAddListItem(item);
}
void MainWindow::OnConnected(std::string const& nsp)
{
QByteArray bytes = m_name.toUtf8();
std::string nickName(bytes.data(),bytes.length());
_io->socket()->emit("add user", nickName);
}
void MainWindow::OnClosed(client::close_reason const& reason)
{
Q_EMIT RequestToggleInputs(false);
}
void MainWindow::OnFailed()
{
Q_EMIT RequestToggleInputs(false);
}
void MainWindow::ToggleInputs(bool loginOrNot)
{
if(loginOrNot)//already login
{
this->findChild<QWidget*>("messageEdit")->setEnabled(true);
this->findChild<QWidget*>("listView")->setEnabled(true);
// this->findChild<QWidget*>("sendBtn")->setEnabled(true);
}
else
{
this->findChild<QWidget*>("messageEdit")->setEnabled(false);
this->findChild<QWidget*>("listView")->setEnabled(false);
// this->findChild<QWidget*>("sendBtn")->setEnabled(false);
ShowLoginDialog();
}
}

View File

@@ -0,0 +1,68 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QListWidget>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QTimer>
#include <sio_client.h>
#include "nicknamedialog.h"
namespace Ui {
class MainWindow;
}
using namespace sio;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public Q_SLOTS:
void SendBtnClicked();
void TypingChanged();
void OnMessageReturn();
protected:
void showEvent(QShowEvent* event);
Q_SIGNALS:
void RequestAddListItem(QListWidgetItem *item);
void RequestRemoveListItem(QListWidgetItem *item);
void RequestToggleInputs(bool loginOrNot);
private Q_SLOTS:
void AddListItem(QListWidgetItem *item);
void RemoveListItem(QListWidgetItem *item);
void ToggleInputs(bool loginOrNot);
void TypingStop();
void NicknameAccept();
void NicknameCancelled();
private:
void OnNewMessage(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnUserJoined(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnUserLeft(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnStopTyping(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnLogin(std::string const& name,message::ptr const& data,bool hasAck,message::list &ack_resp);
void OnConnected(std::string const& nsp);
void OnClosed(client::close_reason const& reason);
void OnFailed();
void ShowLoginDialog();
Ui::MainWindow *ui;
std::unique_ptr<client> _io;
std::unique_ptr<NicknameDialog> m_dialog;
QString m_name;
std::unique_ptr<QTimer> m_timer;
QListWidgetItem *m_typingItem;
};
#endif // MAINWINDOW_H

View File

@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>366</width>
<height>549</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Socket.IO Chat</string>
</property>
<property name="unifiedTitleAndToolBarOnMac">
<bool>false</bool>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QListWidget" name="listView">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>8</x>
<y>11</y>
<width>350</width>
<height>461</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeIncrement">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
<widget class="QLineEdit" name="messageEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>8</x>
<y>480</y>
<width>350</width>
<height>21</height>
</rect>
</property>
<property name="placeholderText">
<string>Type here...</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>366</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections>
<connection>
<sender>messageEdit</sender>
<signal>returnPressed()</signal>
<receiver>MainWindow</receiver>
<slot>OnMessageReturn()</slot>
<hints>
<hint type="sourcelabel">
<x>116</x>
<y>524</y>
</hint>
<hint type="destinationlabel">
<x>30</x>
<y>545</y>
</hint>
</hints>
</connection>
<connection>
<sender>messageEdit</sender>
<signal>textChanged(QString)</signal>
<receiver>MainWindow</receiver>
<slot>TypingChanged()</slot>
<hints>
<hint type="sourcelabel">
<x>73</x>
<y>531</y>
</hint>
<hint type="destinationlabel">
<x>70</x>
<y>510</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>SendBtnClicked(bool)</slot>
<slot>TypingChanged()</slot>
<slot>LoginClicked()</slot>
<slot>OnMessageReturn()</slot>
<slot>SendBtnClicked()</slot>
</slots>
</ui>

View File

@@ -0,0 +1,38 @@
#include "nicknamedialog.h"
#include "ui_nicknamedialog.h"
#include <QTimer>
NicknameDialog::NicknameDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::NicknameDialog)
{
ui->setupUi(this);
}
NicknameDialog::~NicknameDialog()
{
delete ui;
}
const QString& NicknameDialog::getNickname() const
{
return m_nickName;
}
void NicknameDialog::exitApp()
{
QApplication::quit();
}
void NicknameDialog::closeEvent(QCloseEvent *event)
{
QTimer::singleShot(0,this,SLOT(exitApp()));
}
void NicknameDialog::accept()
{
if(this->findChild<QLineEdit*>("nicknameEdit")->text().length()>0)
{
m_nickName = this->findChild<QLineEdit*>("nicknameEdit")->text();
done(QDialog::Accepted);
}
}

View File

@@ -0,0 +1,28 @@
#ifndef NICKNAMEDIALOG_H
#define NICKNAMEDIALOG_H
#include <QDialog>
namespace Ui {
class NicknameDialog;
}
class NicknameDialog : public QDialog
{
Q_OBJECT
public Q_SLOTS:
void accept();
void exitApp();
public:
explicit NicknameDialog(QWidget *parent = 0);
~NicknameDialog();
const QString& getNickname() const;
void closeEvent(QCloseEvent *);
private:
Ui::NicknameDialog *ui;
QString m_nickName;
};
#endif // NICKNAMEDIALOG_H

View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NicknameDialog</class>
<widget class="QDialog" name="NicknameDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>366</width>
<height>151</height>
</rect>
</property>
<property name="windowTitle">
<string>Login</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>30</x>
<y>99</y>
<width>311</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLineEdit" name="nicknameEdit">
<property name="geometry">
<rect>
<x>27</x>
<y>60</y>
<width>311</width>
<height>20</height>
</rect>
</property>
<property name="placeholderText">
<string>Type nickname here...</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>44</x>
<y>23</y>
<width>271</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>What's your nickname?</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>NicknameDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>298</x>
<y>97</y>
</hint>
<hint type="destinationlabel">
<x>298</x>
<y>108</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>NicknameDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>210</x>
<y>125</y>
</hint>
<hint type="destinationlabel">
<x>202</x>
<y>84</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>checkAccept()</slot>
<slot>checkAcc()</slot>
</slots>
</ui>