-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdisplay.c
221 lines (196 loc) · 6.65 KB
/
display.c
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#include <time.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include "terminal.h"
#include "display.h"
#include "chat.h"
#include "main.h"
static pthread_mutex_t lock;
// These variables should only be accessed/modified through locks.
static struct top_data top;
static struct bottom_data bottom;
static bool needs_update;
// Amount of messages to view up from last message.
static int scroll_factor;
void initialize_display(){
if(pthread_mutex_init(&lock, NULL) != 0){
printf("Failed to create lock\r\n");
exit(1);
}
needs_update = true;
scroll_factor = 0;
}
void view_older_messages(){
pthread_mutex_lock(&lock);
int message_length = get_message_length();
if(message_length > 1){
scroll_factor++;
}
pthread_mutex_unlock(&lock);
}
void view_newer_messages(){
pthread_mutex_lock(&lock);
if(scroll_factor != 0){
scroll_factor--;
}
pthread_mutex_unlock(&lock);
}
/**
* Determines the amount of lines need to print the given string to the terminal.
* @param width Number of columns of terminal
* @param string String to be printed
*/
int lines_needed_to_print(int width, struct message *to_display){
int length = strlen(to_display->content);
int size_message_body = (length / width) + 1;
return size_message_body + 1; // add one for the line showing username and date
}
void set_bottom_text(bool on_command_mode, char *text){
pthread_mutex_lock(&lock);
bottom.on_command_mode = on_command_mode;
if(on_command_mode){
strncpy(bottom.text, text, MAX_LENGTH_COMMAND);
}else{
strncpy(bottom.text, text, MAX_LENGTH_MESSAGE);
}
pthread_mutex_unlock(&lock);
}
void print_top_data(int width, int height, char *buffer){
// Print top bar at top left
char to_print[60 + MAX_LENGTH_USERNAME] = {'\0'};
strcat(to_print, "\x1b[;1H"); // move cursor to top left of terminal
strcat(to_print, get_username());
strcat(to_print, " - ");
strcat(to_print, get_ipv4_address());
strcat(to_print, "\r\n");
strncat(buffer, to_print, width);
}
struct message *cannot_print(struct message *last_message, int width, int *lines_available, int *lines_read_buff){
// find the first (oldest) message that cannot fit on the screen
struct message *oldest_msg = last_message;
int lines_read = 0;
while(oldest_msg != NULL && (*lines_available - lines_needed_to_print(width, oldest_msg)) >= 0){
*lines_available -= lines_needed_to_print(width, oldest_msg);
oldest_msg = oldest_msg->previous;
lines_read++;
}
*lines_read_buff = lines_read;
return oldest_msg;
}
void print_middle_data(int width, int height, char *buffer){
int lines_available = height - TOP_LINES - BOTTOM_LINES;
if(lines_available <= 0){
char *to_print = "Not enough space to print everything\r\n";
printf("%s\r\n", to_print);
exit(1);
}
// Get messages
struct message *first_message;
struct message *last_message;
size_t message_length;
get_message_lock(&first_message, &last_message, &message_length);
struct message *oldest_msg = last_message;
int lines_read = 0;
for(int i = 0; i < scroll_factor; i++){
oldest_msg = oldest_msg->previous;
}
int try_available_lines = lines_available;
struct message *new_oldest = cannot_print(oldest_msg, width, &try_available_lines, &lines_read);
if(scroll_factor > 0){
// Did scroll so we are covering a message, find it
struct message *hidden = oldest_msg->next;
if(try_available_lines >= lines_needed_to_print(width, hidden)){
// Cannot scroll
oldest_msg = cannot_print(oldest_msg->next, width, &lines_available, &lines_read);
scroll_factor--;
}else{
lines_available = try_available_lines;
oldest_msg = new_oldest;
}
}else{
lines_available = try_available_lines;
oldest_msg = new_oldest;
}
struct message *message_to_print;
if(oldest_msg == NULL){
// Did not find a message that cannot fit on screen, so we can print everything
message_to_print = first_message;
}else{
// Can fit all message after oldest_msg
message_to_print = oldest_msg->next;
}
while(message_to_print != NULL && lines_read != 0){
// print the username
char *heading = message_to_print->username;
if(strcmp(heading, "System") == 0){
// make red
strcat(buffer, "\e[31m");
}else{
// make blue
strcat(buffer, "\e[34m");
}
strcat(buffer, heading);
// clear coloring
strcat(buffer, "\e[0m");
strcat(buffer, "\r\n");
// print actual message
strcat(buffer, message_to_print->content);
strcat(buffer, "\r\n");
message_to_print = message_to_print->next;
lines_read--;
}
// Fill rest of lines available with empty text
while(lines_available > 0){
strcat(buffer, "\r\n");
lines_available--;
}
// Release information
release_message_lock();
}
void print_bottom_data(int width, int height, char *buffer){
char *text = bottom.text;
int length = strlen(text);
// Byte layout:
// \x1b -- escape character (1 byte)
// [???;???H -- each question mark is one byte. 9 bytes
// end of string character (1 byte)
char cursor_reposition[11] = {0};
// pad with "-"
char *padding_help_text = bottom.on_command_mode ? "COMMAND" : "WRITE";
int padding_amount = width - strlen(padding_help_text);
strcat(buffer, padding_help_text);
for(int i = 0; i < padding_amount; i++){
strcat(buffer, "=");
}
if(length > width){
// not enough space to fit everything
text += length - width + 1; // reserve one space for cursor
sprintf(cursor_reposition, "\x1b[%d;%dH", height, length + 1);
strcat(buffer, text);
}else{
strcat(buffer, text);
sprintf(cursor_reposition, "\x1b[%d;%dH", height, length + 1);
}
strcat(buffer, cursor_reposition);
}
void update_screen(){
int dimensions[2];
get_terminal_dimensions(dimensions);
int width = dimensions[0];
int height = dimensions[1];
// Each line has additional information ("\r\n" and "\x1b[;1H")
char *buffer = calloc(width * height + (height * 5), sizeof(char));
print_top_data(width, height, buffer);
print_middle_data(width, height, buffer);
print_bottom_data(width, height, buffer);
write(STDOUT_FILENO, buffer, strlen(buffer));
free(buffer);
}
void display(){
clear_terminal();
update_screen();
}