FLTK IM Chat Window remote (pop-up) instantiation, GUI speed issues, and graceful socket closure

IDE: Microsoft c++ 2008 express
Platform: Windows 7
Libraries: FLTK and Winsock

Hello, all!

I'm having some problems working out the bugs on my Instant Messenger program and server. The issue my boss would like to see fixed is speed. Specifically, my chat doesn't display instantaneously via the loopback address. Sometimes it can take up to ten seconds for a message to pop up on the chat window, even though the data is being sent instantaneously according to the console. I use some char pointers for buffers, but I've been good to delete them at the end of functions and/or the quit callbacks. Based on what's going on, this is at the GUI level, and I'm not sure how to stabilize FLTK in this regard.

Another issue I'm having (and this is my big issue) is that I can't get the chat window to pop-up when the program is sent a message. It tries to, and takes up half my CPU (or probably one whole core), and freezes the program. It could be because the chat window is instantiated on a sub-thread (I created a separate thread to receive data) instead of the main thread. I've tried using FL::lock and FL::unlock around the instantiation and subsequent code to process the message and just around the instantiation, I've tried giving the chat window its own thread (since I've found out that basic blocking is the best solution for the client; non-blocking and it gets buggy or bloated, depending on the I/O model I use), creating a different Chat Window object for the sub-thread, overloading the constructor to pass the message as an argument and process the data within the constructor, commenting everything but the widgets, commenting everything including the widgets just to pop up a blank window, and nothing seems to work. Here's the relevant code for the ChatWindow class (the spec file, the constructor, and the inline quit and send callbacks) and the ReceiveThread class(the spec file and the main function), with apologies for the volume of code:

ChatWindow.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#ifndef CHATWINDOW_H
#define CHATWINDOW_H
#include <FL/fl.H>
#include <FL/fl_Text_Buffer.H>
#include <FL/fl_Text_Editor.H>
#include <FL/fl_Text_Display.H>
#include <FL/Fl_File_Chooser.H>
#include "BuddyListWindow.h"

#pragma warning(disable:4996)


class ChatWindow : public Fl_Double_Window
{
private:
	bool buddyFlag;
	char *buddyName;
	char *buddyListName;
	char *buddyLogMode;

public:

	ChatWindow(int x, int y, int w, int h, const char *title);  // Constructor Prototype
	~ChatWindow();  // Destructor Prototype	

	// Widgets
	Fl_Menu_Bar *chatMenuBar;
	Fl_Text_Display *chatDisplay;
	Fl_Text_Editor *chatEditor;
	Fl_Box *chatContainer;
	Fl_Return_Button *btnSend;

	// Static bool to determine of Chat is up
	static bool chatFlag;

	// Callback functions and their inline counterparts
	static void about_cb(Fl_Widget *w, void *v);
	inline void about_cb_i();

	static void copy_cb(Fl_Widget *w, void *v);
	inline void copy_cb_i();

	static void cut_cb(Fl_Widget *w, void *v);
	inline void cut_cb_i();

	static void delete_cb(Fl_Widget *w, void *v);
	inline void delete_cb_i();

	static void quit_cb(Fl_Widget *w, void *v);
	inline void quit_cb_i();

	static void paste_cb(Fl_Widget *w, void *v);
	inline void paste_cb_i();

	static void save_cb(Fl_Widget *w, void *v);
	inline void save_cb_i();

	static void saveas_cb(Fl_Widget *w, void *v);
	inline void saveas_cb_i();

	static void send_cb(Fl_Widget *w, void *v);
	inline void send_cb_i();

	// Other functions
	int handle(int e);
	void saveLog(char *chatLog);
};

bool ChatWindow::chatFlag = false;

#endif 


And the relevant functions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Constructor Definition
ChatWindow::ChatWindow(int x, int y, int w, int h, const char *title) : Fl_Double_Window(x, y, w, h, title)
{
	buddyFlag = false;
	buddyName = new char [31];
	buddyListName = new char[44];
	buddyLogMode = new char[12];

	for(int i = 1; i <= buddyListWindow->buddyList->size(); i++)
	{
		if(buddyListWindow->buddyList->selected(i))
		{
			int cnt = 0;
			buddyFlag = true;
			strcpy(buddyListName, (char *)buddyListWindow->buddyList->text(i));

			while(buddyListName[cnt] != '-')
			{
				buddyName[cnt] = buddyListName[cnt];
				cnt++;
			}

			buddyName[cnt] = '\0';
			cnt++;

			for(int i = 0; i < strlen(buddyLogMode); i++)
			{
				if(buddyListName[cnt] == '\0')
				{
					buddyLogMode[i] = buddyListName[cnt];
					break;
				}
				else
				{
					buddyLogMode[i] = buddyListName[cnt];
					cnt++;
				}
			}

			break;
		}
	}

	make_current();
	color(fl_rgb_color(200, 200, 255));
	size_range(this->w(), this->h());

	begin();
		chatMenuBar = new Fl_Menu_Bar(0, 0, 640, 20);
		chatMenuBar->copy(chatMenu, this);
		chatMenuBar->color(FL_BLACK);
		chatMenuBar->textcolor(FL_WHITE);
		chatMenuBar->textsize(12);

		chatDisplay = new Fl_Text_Display(0, chatMenuBar->y() + chatMenuBar->h(), 640, 270);
		chatDisplay->wrap_mode(1, 0);
		txtDisplayBuffy = new Fl_Text_Buffer();
		chatDisplay->buffer(txtDisplayBuffy);

		chatContainer = new Fl_Box(10, chatDisplay->y() + chatDisplay->h() + 50, 610, 50);
		chatContainer->box(FL_DOWN_BOX);
		chatContainer->color(FL_WHITE);

		chatEditor = new Fl_Text_Editor(10, chatDisplay->y() + chatDisplay->h() + 50, 525, 50);
		chatEditor->wrap_mode(1, 0);
		txtEditorBuffy = new Fl_Text_Buffer();
		chatEditor->buffer(txtEditorBuffy);
		chatEditor->take_focus();

		btnSend = new Fl_Return_Button(2 + chatEditor->x() + chatEditor->w(), chatDisplay->y() + chatDisplay->h() + 55,
			                           75, 40, "Send");
		btnSend->color(FL_BLACK);
		btnSend->color2(FL_BLACK);
		btnSend->labelcolor(FL_WHITE);
		btnSend->callback(send_cb, this);
		
	end();
	callback(quit_cb, this);
	show();
	ChatWindow::chatFlag = true;
}

void ChatWindow::quit_cb_i()
{
	buddyListWindow->btnConnect->value(0);
	ChatWindow::chatFlag = false;

	if(buddyName != NULL)
	{
		delete [] buddyName;
		buddyName = NULL;
	}

	if(buddyListName != NULL)
	{
		delete [] buddyListName;
		buddyListName = NULL;
	}

	if(buddyLogMode != NULL)
	{
		delete [] buddyLogMode;
		buddyLogMode = NULL;
	}

	hide();
}

void ChatWindow::send_cb_i()
{
	int action = 0;

	if(buddyFlag)
	{
		sendData.friendname.clear();
		sendData.friendname.append(buddyName);
	}

	if(!strcmp(buddyLogMode, "Offline"))
	{
		OfflineWindow *offlineWindow = new OfflineWindow(buddyName, 220, 240, 400, 200, "User is offline");
	}

	sendData.sendMessage.clear();
	sendData.sendMessage.append((char *)txtEditorBuffy->text());

	txtDisplayBuffy->append(sendData.username.c_str());
	txtDisplayBuffy->append(": ");
	txtDisplayBuffy->append(sendData.sendMessage.c_str());
	chatWindow->chatDisplay->textcolor(FL_RED);
	chatWindow->chatDisplay->textfont(FL_HELVETICA | FL_BOLD);
	txtEditorBuffy->clear_rectangular(0, chatWindow->chatEditor->w(), 0, chatWindow->chatEditor->h() *100);
	chatWindow->chatEditor->take_focus();
	if (Fl::event_key() != FL_Enter)
		txtDisplayBuffy->append("\n");

	if(strcmp(sendData.username.c_str(), "offline") && strcmp(sendData.password.c_str(), "offline"))
	{
		buddyListWindow->parseSend();

		action = send(logonSocket, buffyTheMessageSender, strlen(buffyTheMessageSender), 0);
		if(action == SOCKET_ERROR)
		{
			cout << "\nERROR: Unable to send data on thread. Code " << WSAGetLastError() << endl;
			fl_alert("Could not send Data.");
		}
	}

		
}


I seem to be running out of space, here, so I'll post the code for the ReceiveThread and my third issue in another post below this one.
Here is the ReceiveThread class where the ChatWindow would be called if the user receives a message.

ReceiveThread.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#ifndef RECEIVETHREAD_H
#define RECEIVETHREAD_H
#include "BuddyListWindow.h"
#include "ChatWindow.h"
#include "OfflineWindow.h"

class ReceiveThread
{
private:
	char friendBuffer[30];
	char friendLogBuffer[8];
	char recvBuffer[1025];
	char *friendListLine;

public:

	// Static boolean flag variable
	static bool isTurnedOn;

	// Constructor
	ReceiveThread();

	// Destructor
	virtual ~ReceiveThread();

	// Static startup thread
	static unsigned __stdcall staticReceiveThreadStartup(void *v);

	// Main routine
	void receiveThreadMain();

	// Sub-routines
	void initializeReceive();
	void parseRecv();
};

bool ReceiveThread::isTurnedOn = true;

#endif 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
void ReceiveThread::receiveThreadMain()
{
	RecvData recvDataReadOnly;
	bool buddyListCopied = false;
	int action = 0;
	
	while(isConnected)
	{
		if(!ReceiveThread::isTurnedOn)
			break;
		initializeReceive();
		action = recv(logonSocket, buffyTheMessageReceiver, sizeof(buffyTheMessageReceiver), 0);
		if (action > 0)
		{
			cout << buffyTheMessageReceiver;
			action = 0;
		} // if
		else
		{
			cout << "ERROR: Unable to receive data Code " << WSAGetLastError();
			action = 0;
		} // else
		parseRecv();

		// Code pertaining to processing buddyList data, to be shown upon request

		if(ChatWindow::chatFlag)
		{
			if(!strcmp(recvData.friendname.c_str(), sendData.username.c_str()))
			{
				chatWindow->chatDisplay->textcolor(FL_BLUE);
				chatWindow->chatDisplay->textfont(FL_HELVETICA | FL_BOLD);
				txtDisplayBuffy->append(recvData.username.c_str());
				txtDisplayBuffy->append(": ");
				txtDisplayBuffy->append(recvData.recvMessage.c_str());
				txtEditorBuffy->clear_rectangular(0, chatWindow->chatEditor->w(), 0, chatWindow->chatEditor->h() *100);
				chatWindow->chatEditor->take_focus();
				if (Fl::event_key() != FL_Enter)
					txtDisplayBuffy->append("\n");
			}
		}
		else
		{
			if(!strcmp(recvData.friendname.c_str(), sendData.username.c_str()))
			{
				chatWindow = new ChatWindow(100, 100, 640, 480, "Say Something!");
				chatWindow->chatDisplay->textcolor(FL_BLUE);
				chatWindow->chatDisplay->textfont(FL_HELVETICA | FL_BOLD);
				txtDisplayBuffy->append(recvData.username.c_str());
				txtDisplayBuffy->append(": ");
				txtDisplayBuffy->append(recvData.recvMessage.c_str());
				txtEditorBuffy->clear_rectangular(0, chatWindow->chatEditor->w(), 0, chatWindow->chatEditor->h() *100);
				chatWindow->chatEditor->take_focus();
				if (Fl::event_key() != FL_Enter)
					txtDisplayBuffy->append("\n");
			}
		}
	}

	if(friendListLine != NULL)
	{
		delete [] friendListLine;
		friendListLine = NULL;
	}
}


My third issue is a bit on the n00bish side, and it has to do with the graceful shutdown of sockets on the server side. I think my problem is that I use a socket array, since on a one-to-one level, I have no problems. My main problem seems to be nullifying the socket address from which the client disconnected without causing a socket error and crashing the server and all other clients connected to it. Here is the code for the FD_CLOSE case, as well as the relevant variables, along with the closeWinsock() function on the IM:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const int maxClients = 5;
int clientNumber = 0;
SOCKET clientSockets[maxClients - 1];

case FD_CLOSE:
				shutdown(wParam, SD_SEND);
				recv(wParam, buffyTheMessageReceiver, sizeof(buffyTheMessageReceiver), 0);
				closesocket(wParam);

				for(int i = 0; i < maxClients; i++)
				{
					clientSockets[i] = clientSockets[i + 1];
				}
				break;


1
2
3
4
5
6
7
8
9
10
11
12
void BuddyListWindow::closeWinsock()
{
	u_long nonblock = 1;

	ioctlsocket(logonSocket, FIONBIO, &nonblock);

	shutdown(logonSocket, SD_SEND);
	recv(logonSocket, buffyTheMessageReceiver, sizeof(buffyTheMessageReceiver), 0);
	closesocket(logonSocket);

	WSACleanup();
}


So, does anyone have any suggestions as to how I might resolve these issues? I'm really at my wits end with the GUI issues especially.
Found out why from our resident FLTK expert. You can only call show() from the main thread. If you want to call it from a subthread, write a callback and use Fl::awake() to call it.
Topic archived. No new replies allowed.