Browse Source

init the project

ZengGengSen 2 năm trước cách đây
commit
341f700ad4

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+/cmake-build-*
+/.cache
+/compile_commands.json
+/asio-1.28.0

+ 13 - 0
CMakeLists.txt

@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.18)
+
+project(netscore)
+
+# add_executable(${PROJECT_NAME} main.cpp ui.c)
+add_executable(${PROJECT_NAME} main.cpp ui.c)
+# add_executable(netscore-server server.cpp)
+
+find_package(SDL2 REQUIRED)
+
+target_link_libraries(${PROJECT_NAME} PRIVATE SDL2::SDL2 SDL2_ttf SDL2_image)
+
+add_executable(client-test client.cpp)

+ 16 - 0
Makefile

@@ -0,0 +1,16 @@
+build-performance = release
+
+all: build
+
+$(build-performance): CMakeLists.txt
+	cmake -GNinja -S. -Bcmake-build-$(build-performance) -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
+
+compile_commands.json: $(build-performance)
+	cp cmake-build-$(build-performance)/compile_commands.json .
+
+build: compile_commands.json
+	ninja -Ccmake-build-$(build-performance)
+
+clean:
+	rm -r cmake-build-$(build-performance)
+

+ 25 - 0
client.cpp

@@ -0,0 +1,25 @@
+#include <iostream>
+#include <asio.hpp>
+
+int main()
+{
+    asio::io_context ioContext;
+    asio::ip::udp::socket socket(ioContext, asio::ip::udp::endpoint(asio::ip::udp::v4(), 8032));
+
+    // 开启广播选项,允许接收广播消息
+    socket.set_option(asio::socket_base::broadcast(true));
+
+    asio::ip::udp::endpoint senderEndpoint;
+    char buffer[1024];
+
+    while (true) {
+        std::size_t bytesRead = socket.receive_from(asio::buffer(buffer, 1024), senderEndpoint);
+
+        // 将接收到的数据转换为字符串
+        std::string message(buffer, bytesRead);
+
+        std::cout << "Received message: " << message << std::endl;
+    }
+
+    return 0;
+}

BIN
font/msyh.ttc


BIN
font/msyhbd.ttc


BIN
font/msyhl.ttc


BIN
img/bg.png


BIN
img/big/0.png


BIN
img/big/1.png


BIN
img/big/2.png


BIN
img/big/3.png


BIN
img/big/4.png


BIN
img/big/5.png


BIN
img/big/6.png


BIN
img/big/7.png


BIN
img/big/8.png


BIN
img/big/9.png


BIN
img/big@2/0@2x(1).png


BIN
img/big@2/0@2x(2).png


BIN
img/big@2/0@2x(5).png


BIN
img/big@2/2@2x(1).png


BIN
img/box_0.png


BIN
img/box_1.png


BIN
img/p1_win_0.png


BIN
img/p1_win_1.png


BIN
img/p2_win_0.png


BIN
img/p2_win_1.png


BIN
img/small/0.png


BIN
img/small/1.png


BIN
img/small/2.png


BIN
img/small/3.png


BIN
img/small/4.png


BIN
img/small/5.png


BIN
img/small/6.png


BIN
img/small/7.png


BIN
img/small/8.png


BIN
img/small/9.png


BIN
img/底.png


+ 335 - 0
main.cpp

@@ -0,0 +1,335 @@
+#include <SDL_events.h>
+#include <SDL_mouse.h>
+#include <SDL_mutex.h>
+#include <SDL_video.h>
+#include <asio/socket_base.hpp>
+#include <cstdint>
+#include <iostream>
+#include <condition_variable>
+#include <asio.hpp>
+#include <mutex>
+#include "ui.h"
+
+using asio::ip::udp;
+
+int32_t bytesToInt(const char *bytes) {
+    int32_t result = uint8_t(bytes[0]) << 24 |
+                     uint8_t(bytes[1]) << 16 |
+                     uint8_t(bytes[2]) <<  8 |
+                     uint8_t(bytes[3]) <<  0;
+    return result;
+}
+
+class Client {
+public:
+    Client(struct Application *a, asio::io_context& ioContext, uint16_t udpPort)
+        : app(a),
+        socket(ioContext),
+        udpSocket(ioContext, udp::endpoint(udp::v4(), udpPort)) {
+        udpSocket.set_option(asio::socket_base::broadcast(true));
+        // socket.connect(serverEndpoint);
+    }
+
+    void start() {
+        udpReceive();
+        // receiveHeader();
+    }
+
+    void disconnect() {
+        socket.close();
+        udpSocket.close();
+        std::cout << "Disconnected from the server" << std::endl;
+    }
+
+private:
+    void udpReceive() {
+        udpSocket.async_receive_from(
+            asio::buffer(udpData, 256), senderEndpoint,
+            [this](std::error_code ec, std::size_t length) {
+                if (!ec && length > 0) {
+                    std::string msg(udpData, length);
+                    
+                    std::cout << "Received message: " << msg << std::endl;
+                    std::cout << "Sender IP: " << senderEndpoint.address().to_string() << std::endl;
+
+                    if (msg == "StreamControlTag") {
+                        std::cout << "Connect to server" << std::endl;
+                        asio::ip::tcp::endpoint serverEndpoint(senderEndpoint.address(), 8031);
+                        socket.connect(serverEndpoint);
+                        receiveHeader();
+                    }
+                }
+            }
+        );
+    }
+
+    void receiveHeader() {
+        std::cout << "start Received: Header" << std::endl;
+        asio::async_read(socket, asio::buffer(headerBuffer), [this](std::error_code ec, std::size_t length) {
+            if (!ec) {
+                std::size_t messageLength = parseHeader();
+                receiveMessage(messageLength);
+            }
+        });
+    }
+
+    std::size_t parseHeader() {
+        return bytesToInt(headerBuffer.data());
+    }
+
+    void receiveMessage(std::size_t messageLength) {
+        bodyBuffer.resize(messageLength);
+        asio::async_read(socket, asio::buffer(bodyBuffer), [this](std::error_code ec, std::size_t length) {
+            if (!ec) {
+                std::cout << "Received: Message" << std::endl;
+                parseMessage();
+                receiveHeader(); // Continue to receive the next message
+            }
+        });
+    }
+
+    void parseMessage() {
+        // Parse the received message according to the specified format
+        std::size_t offset = 0;
+
+        // Extracting the message components
+        uint8_t messageType = (uint8_t)(bodyBuffer[offset]);
+        offset += 1;
+
+        if (messageType == 1) {
+            int32_t gameRound = bytesToInt(&bodyBuffer[offset]); 
+            offset += 4;
+
+            int32_t player1NameLength = bytesToInt(&bodyBuffer[offset]);
+            offset += 4;
+
+            std::string player1Name(&bodyBuffer[offset], player1NameLength);
+            offset += player1NameLength;
+
+            int32_t player2NameLength = bytesToInt(&bodyBuffer[offset]);
+            offset += 4;
+
+            std::string player2Name(&bodyBuffer[offset], player2NameLength);
+            offset += player2NameLength;
+
+            int32_t player1Score = bytesToInt(&bodyBuffer[offset]);
+            offset += 4;
+
+            int32_t player2Score = bytesToInt(&bodyBuffer[offset]);
+            offset += 4;
+
+            updateApplication(app, player1Name.c_str(), player2Name.c_str(), gameRound, player1Score, player2Score);
+        }
+    }
+
+private:
+    struct Application *app;
+    asio::ip::udp::socket udpSocket;
+    udp::endpoint senderEndpoint;    
+    char udpData[256];
+
+    asio::ip::tcp::socket socket;
+    std::array<char, 4> headerBuffer;
+    std::vector<char> bodyBuffer;
+};
+
+int main() {
+    struct Application application, *app = &application;
+    if (initApplication(app)) {
+        printf("[Error]: initialize application failure: %s\n", app->errorString);
+        return -1;
+    }
+
+    printf("width: %d, height: %d\n", app->winWidth, app->winHeight);
+
+    std::mutex cv_mutex;
+    std::condition_variable cv;
+
+    asio::io_context ioContext;
+
+    bool quit = false;
+    bool network = true;
+    SDL_Event event;
+
+    std::thread clientThread = std::thread([&]() {
+        try {
+            constexpr uint16_t udpPort = 8032;
+            /// asio::ip::tcp::endpoint serverEndpoint(asio::ip::make_address("192.168.1.80"), 8031);
+
+            while (!quit) {
+                std::unique_lock<std::mutex> lk(cv_mutex);
+                std::cout << "start Client Loop" << std::endl;
+
+                while (network) {  // 使用 lambda 检查条件
+                    if (quit) break;
+
+                    // Client client(app, ioContext, serverEndpoint, udpPort);
+                    Client client(app, ioContext, udpPort);
+
+                    client.start();
+                    ioContext.run();
+                    client.disconnect();
+                }
+
+                cv.wait(lk, [&]{ return network; });  // 等待直到 network 为 true 
+            }
+        } catch (const std::exception& e) {
+            std::cerr << "Exception: " << e.what() << std::endl;
+        }
+    });
+
+    SDL_ShowCursor(SDL_DISABLE);
+    while (!quit) {
+        SDL_LockMutex(app->mutex);
+
+        while (SDL_PollEvent(&event)) {
+            if (event.type == SDL_QUIT) {
+                quit = true;
+                network = true;
+            } else if (event.type == SDL_MOUSEBUTTONDOWN) {
+                if (event.button.button == SDL_BUTTON_RIGHT) {
+                    quit = true;
+                    network = true;
+                }
+            } else if (event.type == SDL_WINDOWEVENT) {
+                if (event.type == SDL_WINDOWEVENT_FOCUS_LOST) {
+                    quit = true;
+                    network = true;
+                }
+            } else if (event.type == SDL_KEYDOWN) {
+                switch(event.key.keysym.scancode) {
+                    case SDL_SCANCODE_O:
+                        network = !network;
+
+                        if (network == false) {
+                            ioContext.stop();
+                            updateApplicationMode(app, 1);
+                            updateApplication(app, "", "", 0, 0, 0);
+                        } else {
+                            ioContext.restart();
+                            updateApplicationMode(app, 0);
+                            updateApplication(app, "", "", 0, 0, 0);
+                            cv.notify_all();
+                        }
+                        break;
+
+                    default:
+                        if (!network) {
+                            switch(event.key.keysym.scancode) {
+                                case SDL_SCANCODE_C:
+                                    updateApplication(app, "", "", 0, 0, 0);
+                                    break;
+
+                                case SDL_SCANCODE_S:
+                                    updateApplication(app, "", "", app->roundNum, app->p2Num, app->p1Num);
+
+                                    app->p1Win = 0;
+                                    app->p2Win = 0;
+
+                                    break;
+
+                                case SDL_SCANCODE_KP_1:
+                                    if (app->p1Num == 99) {
+                                        app->p1Num  = 0;
+                                    } else {
+                                        ++app->p1Num;
+                                    }
+
+                                    updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num);
+
+                                    app->p1Win = 1;
+                                    app->p2Win = 0;
+                                    break;
+
+                                case SDL_SCANCODE_KP_2:
+                                    if (app->p1Num > 0) {
+                                        --app->p1Num;
+                                    }
+
+                                    updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num);
+                                    break;
+
+                                case SDL_SCANCODE_KP_3:
+                                    updateApplication(app, "", "", app->roundNum, 0, app->p2Num);
+
+                                    app->p1Win = 0;
+                                    app->p2Win = 0;
+                                    break;
+
+                                case SDL_SCANCODE_KP_4:
+                                    if (app->p2Num == 99) {
+                                        app->p2Num = 0;
+                                    } else {
+                                        ++app->p2Num;
+                                    }
+
+                                    updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num);
+                                    app->p1Win = 0;
+                                    app->p2Win = 1;
+
+                                    break;
+
+                                case SDL_SCANCODE_KP_5:
+                                    if (app->p2Num > 0) {
+                                        --app->p2Num;
+                                    }
+
+                                    updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num);
+                                    break;
+
+                                case SDL_SCANCODE_KP_6:
+                                    updateApplication(app, "", "", app->roundNum, app->p1Num, 0);
+
+                                    app->p1Win = 0;
+                                    app->p2Win = 0;
+                                    break;
+
+                                case SDL_SCANCODE_KP_7:
+                                    if (app->roundNum == 99) {
+                                        app->roundNum = 0;
+                                    } else {
+                                        ++app->roundNum;
+                                    }
+
+                                    updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num);
+                                    break;
+
+                                case SDL_SCANCODE_KP_8:
+                                    if (app->roundNum > 0) {
+                                        --app->roundNum;
+                                    }
+
+                                    updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num);
+                                    break;
+
+                                case SDL_SCANCODE_KP_9:
+                                    updateApplication(app, "", "", 0, app->p1Num, app->p2Num);
+                                    break;
+
+                                default:
+                                    break;
+                            }
+                        }
+                }
+            }
+        }
+
+        if (app->updateRequire) {
+            drawApplication(app);
+            app->updateRequire = 0;
+        }
+
+        SDL_UnlockMutex(app->mutex);
+        SDL_Delay(166);
+    }
+
+    // 停止客户端并等待线程结束
+    ioContext.stop();
+    cv.notify_all();
+    if (clientThread.joinable())
+        clientThread.join();
+
+    termApplication(app);
+    return 0;
+}
+

+ 0 - 0
server.cpp


+ 420 - 0
ui.c

@@ -0,0 +1,420 @@
+#include <SDL_error.h>
+#include <SDL_events.h>
+#include <SDL_mutex.h>
+#include <SDL_render.h>
+#include <SDL_stdinc.h>
+#include <SDL_video.h>
+#include <string.h>
+#include "ui.h"
+
+#define MAX_PATH_NUM 1024
+
+const char *resources = "/home/stone/Project/netscore/";
+
+// 绘制数字
+static void drawBigNumber(SDL_Renderer* renderer, SDL_Texture* numberTexture, int x, int y) {
+    static const int NumberWidth = 344, NumberHeight = 573;
+    SDL_Rect dstRect = {x, y, NumberWidth, NumberHeight};
+    SDL_RenderCopy(renderer, numberTexture, NULL, &dstRect);
+}
+
+// 绘制数字
+static void drawSmallNumber(SDL_Renderer* renderer, SDL_Texture* numberTexture, int x, int y) {
+    static const int NumberWidth = 290, NumberHeight = 484;
+    SDL_Rect dstRect = {x, y, NumberWidth, NumberHeight};
+    SDL_RenderCopy(renderer, numberTexture, NULL, &dstRect);
+}
+
+static void drawBox(SDL_Renderer* renderer, SDL_Texture* boxTexture, int x, int y) {
+    static const int NumberWidth = 892, NumberHeight = 1044;
+    SDL_Rect dstRect = {x, y, NumberWidth, NumberHeight};
+    SDL_RenderCopy(renderer, boxTexture, NULL, &dstRect);
+}
+
+static void drawWin(SDL_Renderer* renderer, SDL_Texture* winTexture, int x, int y) {
+    static const int NumberWidth = 639, NumberHeight = 193;
+    SDL_Rect dstRect = {x, y, NumberWidth, NumberHeight};
+    SDL_RenderCopy(renderer, winTexture, NULL, &dstRect);
+}
+
+static void drawNameTexture(struct Application *app, SDL_Texture *texture, int x, int y) {
+    static const int NameWidth = 1120, NameHeight = 118;
+
+    // 获取纹理的宽度和高度
+    int textureWidth, textureHeight;
+    SDL_QueryTexture(texture, NULL, NULL, &textureWidth, &textureHeight);
+
+    // 计算居中显示时的位置
+    int centerX = x + (NameWidth - textureWidth) / 2;
+    int centerY = y + (NameHeight - textureHeight) / 2;
+
+    // 创建目标矩形
+    SDL_Rect dstRect = { centerX, centerY, textureWidth, textureHeight };
+
+    SDL_RenderCopy(app->renderer, texture, NULL, &dstRect);
+}
+
+static SDL_Texture* loadTexture(struct Application *app, const char *path) {
+    SDL_Surface* surface = IMG_Load(path);
+    if (!surface) {
+        return NULL;
+    }
+
+    SDL_Texture* texture = SDL_CreateTextureFromSurface(app->renderer, surface);
+    SDL_FreeSurface(surface);
+    return texture;
+}
+
+int initApplication(struct Application *app) {
+    int i;
+
+    app->ret = 0;
+    app->errorString[0] = '\0';
+
+    app->initialized = 0;
+
+    app->window = NULL;
+    app->winWidth = 0;
+    app->winHeight = 0;
+
+    app->renderer = NULL;
+
+    app->bgTexture = NULL;
+    
+    for (i = 0; i < 10; i++)
+        app->bigNumTextures[i] = NULL;
+    
+    for (i = 0; i < 10; i++)
+        app->smallNumTextures[i] = NULL;
+
+    app->nameFont = NULL;
+
+    app->p1NameTexture = app->p2NameTexture = NULL;
+
+    for (i = 0; i < 2; i++)
+        app->p1WinTexture[i] = app->p2WinTexture[i] = NULL;
+
+    app->scoreHighLightTexture[0] = app->scoreHighLightTexture[1] = NULL;
+
+    app->p1Name[0] = '\0';
+    app->p2Name[0] = '\0';
+    app->roundNum = app->roundNumSt = app->roundNumNd = 0;
+    app->p1Num = app->p1NumSt = app->p1NumNd = 0;
+    app->p2Num = app->p2NumSt = app->p2NumNd = 0;
+    app->p1Win = app->p2Win = 0;
+    app->isKey = 0;
+
+    app->updateRequire = 1;
+    app->mutex = NULL;
+
+    if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
+        snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: SDL_Init: %s", SDL_GetError());
+        return -1;
+    }
+    app->initialized = 1;
+
+    if (TTF_Init() == -1) {
+        snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: TTF_Init: %s", TTF_GetError());
+        return -1;
+    }
+
+    app->window = SDL_CreateWindow("计分板", 0, 0, 3840, 1100, SDL_WINDOW_FULLSCREEN);
+    if (!app->window) {
+        snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: SDL_CreateWindow: %s", SDL_GetError());
+        return -1;
+    }
+
+    SDL_GetWindowSize(app->window, &app->winWidth, &app->winHeight);
+
+    app->renderer = SDL_CreateRenderer(app->window, -1, SDL_RENDERER_ACCELERATED);
+    if (!app->renderer) {
+        snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: SDL_CreateRenderer: %s", SDL_GetError());
+        return -1;
+    }
+    
+    // 加载背景图片 
+    {
+        char path[MAX_PATH_NUM];
+        snprintf(path, MAX_PATH_NUM, "%s%s%s", resources, "img/", "bg.png");
+        
+        SDL_Surface* surface = IMG_Load(path);
+        if (!surface) {
+            snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: Load bg image %s: %s", path, IMG_GetError());
+            return -1;
+        }
+        app->bgTexture = SDL_CreateTextureFromSurface(app->renderer, surface);
+        SDL_FreeSurface(surface);
+    }
+
+    for (i = 0; i < 10; i++) {
+        char path[MAX_PATH_NUM];
+        snprintf(path, MAX_PATH_NUM, "%s%s/%d.png", resources, "img/big", i);
+
+        app->bigNumTextures[i] = loadTexture(app, path);
+        if (!app->bigNumTextures[i]) {
+            snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: Load big numer image %s: %s", path, IMG_GetError());
+            return -1;
+        }
+    }
+
+    for (i = 0; i < 10; i++) {
+        char path[MAX_PATH_NUM];
+        snprintf(path, MAX_PATH_NUM, "%s%s/%d.png", resources, "img/small", i);
+        
+        app->smallNumTextures[i] = loadTexture(app, path);
+        if (!app->smallNumTextures[i]) {
+            snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: Load small numer image %s: %s", path, IMG_GetError());
+            return -1;
+        }
+    }
+
+    {
+        char path[MAX_PATH_NUM];
+        snprintf(path, sizeof(path), "%s%s%s", resources, "font/", "msyh.ttc");
+        app->nameFont = TTF_OpenFont(path, 120);
+        if (app->nameFont == NULL) {
+            snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: Load font path %s: %s", path, TTF_GetError());
+            return -1;
+        }
+    }
+
+    for (i = 0; i < 2; ++i) {
+        char path[MAX_PATH_NUM];
+        snprintf(path, sizeof(path), "%s%s%s_%d.png", resources, "img/", "box", i);
+
+        app->scoreHighLightTexture[i] = loadTexture(app, path);
+        if (!app->scoreHighLightTexture[i]) {
+            snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: Load box image %s: %s", path, IMG_GetError());
+            return -1;
+        }
+    } 
+   
+    for (i = 0; i < 2; ++i) {
+        char path[MAX_PATH_NUM];
+
+        snprintf(path, sizeof(path), "%s%s%s_%d.png", resources, "img/", "p1_win", i);
+        app->p1WinTexture[i] = loadTexture(app, path);
+        if (!app->p1WinTexture[i]) {
+            snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: Load p1_win image %s: %s", path, IMG_GetError());
+            return -1;
+        }
+
+        snprintf(path, sizeof(path), "%s%s%s_%d.png", resources, "img/", "p2_win", i);
+        app->p2WinTexture[i] = loadTexture(app, path);
+        if (!app->p2WinTexture[i]) {
+            snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: Load p2_win image %s: %s", path, IMG_GetError());
+            return -1;
+        }
+    }
+
+    app->mutex = SDL_CreateMutex();
+    if (app->mutex == NULL) {
+        snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: create mutex error: %s", SDL_GetError());
+        return -1;
+    }
+
+    return 0;
+}
+
+void drawApplication(struct Application *app) {
+    // 清空渲染器
+    SDL_RenderClear(app->renderer);
+        
+    SDL_Color textColor = { 255, 255, 255 }; // 白色
+
+    SDL_Surface* surface = NULL;
+
+    {
+        SDL_Surface* surface = NULL;
+
+        if (strnlen(app->p1Name, sizeof(app->p1Name)) != 0) {
+            surface = TTF_RenderUTF8_Solid(app->nameFont, app->p1Name, textColor);
+            if (!surface) {
+                snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: create utf-8 surface error %s: %s", app->p1Name, TTF_GetError());
+            }
+        }
+
+        if (app->p1NameTexture)
+            SDL_DestroyTexture(app->p1NameTexture);
+        app->p1NameTexture = NULL;
+
+        if (surface) {
+            app->p1NameTexture = SDL_CreateTextureFromSurface(app->renderer, surface);
+            if (!app->p1NameTexture) {
+                snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: create texture error %s", SDL_GetError());
+            }
+            
+            SDL_FreeSurface(surface);
+        }
+    }
+    {
+        if (strnlen(app->p2Name, sizeof(app->p2Name)) != 0) {
+            surface = TTF_RenderUTF8_Solid(app->nameFont, app->p2Name, textColor);
+            if (!surface) {
+                snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: create utf-8 surface error %s: %s", app->p2Name, TTF_GetError());
+            }
+        }
+
+        if (app->p2NameTexture)
+            SDL_DestroyTexture(app->p2NameTexture);
+        app->p2NameTexture = NULL;
+
+        if (surface) {
+            app->p2NameTexture = SDL_CreateTextureFromSurface(app->renderer, surface);
+            if (!app->p2NameTexture) {
+                snprintf(app->errorString, sizeof(app->errorString), "[ERROR]: create texture error %s", SDL_GetError());
+            }
+            
+            SDL_FreeSurface(surface);
+        }
+    }
+
+    // 绘制背景图片
+    SDL_RenderCopy(app->renderer, app->bgTexture, NULL, NULL);
+  
+    drawBox(app->renderer, app->scoreHighLightTexture[app->isKey], 1475, 29);
+
+    if (app->p1NameTexture)
+        drawNameTexture(app, app->p1NameTexture, 233, 136);
+    if (app->p2NameTexture)
+        drawNameTexture(app, app->p2NameTexture, 2490, 136);
+    
+    drawSmallNumber(app->renderer, app->smallNumTextures[app->roundNumSt], 1589, 409);
+    drawSmallNumber(app->renderer, app->smallNumTextures[app->roundNumNd], 1962, 409);
+
+    drawBigNumber(app->renderer, app->bigNumTextures[app->p1NumSt], 455, 317);
+    drawBigNumber(app->renderer, app->bigNumTextures[app->p1NumNd], 896, 317);
+
+    drawBigNumber(app->renderer, app->bigNumTextures[app->p2NumSt], 2600, 317);
+    drawBigNumber(app->renderer, app->bigNumTextures[app->p2NumNd], 3041, 317);
+
+    drawWin(app->renderer, app->p1WinTexture[app->p1Win], 528, 883);
+    drawWin(app->renderer, app->p2WinTexture[app->p2Win], 2673, 883);
+
+    SDL_RenderPresent(app->renderer);
+}
+
+void termApplication(struct Application *app) {
+    if (app->initialized == 0)
+        return;
+
+    int i;
+
+    if (app->mutex)
+        SDL_DestroyMutex(app->mutex);
+
+    for (i = 0; i < 2; i++)
+        if (app->scoreHighLightTexture[i])
+            SDL_DestroyTexture(app->scoreHighLightTexture[i]);
+
+    for (i = 0; i < 2; i++) {
+        if (app->p1WinTexture[i])
+            SDL_DestroyTexture(app->p1WinTexture[i]);
+        if (app->p2WinTexture[i])
+            SDL_DestroyTexture(app->p2WinTexture[i]);
+    }
+
+    if (app->p1NameTexture)
+        SDL_DestroyTexture(app->p1NameTexture);
+
+    if (app->p2NameTexture)
+        SDL_DestroyTexture(app->p2NameTexture);
+
+    if (app->nameFont)
+        TTF_CloseFont(app->nameFont);
+
+    for (i = 0; i < 10; i++)
+        if (app->smallNumTextures[i])
+            SDL_DestroyTexture(app->smallNumTextures[i]);
+
+    for (i = 0; i < 10; i++)
+        if (app->bigNumTextures[i])
+            SDL_DestroyTexture(app->bigNumTextures[i]);
+
+    if (app->bgTexture)
+        SDL_DestroyTexture(app->bgTexture);
+
+    if (app->renderer)
+        SDL_DestroyRenderer(app->renderer);
+
+    if (app->window)
+        SDL_DestroyWindow(app->window);
+    
+    if (TTF_WasInit())
+        TTF_Quit();
+    
+    SDL_Quit();
+    app->initialized = 0;
+}
+
+int updateApplicationMode(struct Application *app, uint8 isKey) {
+    app->isKey = isKey;
+    return 0;
+}
+
+int updateApplication(struct Application *app, const char *p1Name, const char *p2Name, int roundNum, int p1Score, int p2Score) {
+    printf("%s, %s, %d, %d, %d\n", p1Name, p2Name, roundNum, p1Score, p2Score);
+    SDL_LockMutex(app->mutex);
+
+    if ((strcmp(p1Name, app->p1Name) == 0 && strcmp(p2Name, app->p2Name) == 0) || 
+        (strcmp(p1Name, app->p2Name) == 0 && strcmp(p2Name, app->p1Name))) {
+        if (p1Score - app->p1Num == 1 && p2Score == app->p2Num) {
+            app->p1Win = 1;
+        } else {
+            app->p1Win = 0;
+        }
+
+        if (p1Score == app->p1Num && p2Score - app->p2Num == 1) {
+            app->p2Win = 1;
+        } else {
+            app->p2Win = 0;
+        }
+    }
+
+    if (p1Name != app->p1Name && strcmp(p1Name, app->p1Name) != 0 && strlen(p1Name) <= 30) {
+        memset(app->p1Name, 0, sizeof(app->p1Name));
+        strncpy(app->p1Name, p1Name, sizeof(app->p1Name) - 1);
+    }
+
+    if (p2Name != app->p2Name && strcmp(p2Name, app->p2Name) != 0 && strlen(p2Name) <= 30) {
+        memset(app->p2Name, 0, sizeof(app->p2Name));
+        strncpy(app->p2Name, p2Name, sizeof(app->p2Name) - 1);
+    }
+    
+    app->roundNum = roundNum % 100;
+    app->roundNumSt = (app->roundNum / 10) % 10;
+    app->roundNumNd = app->roundNum % 10;
+
+    app->p1Num = p1Score % 100;
+    app->p1NumSt = (app->p1Num / 10) % 10;
+    app->p1NumNd = app->p1Num % 10;
+
+    app->p2Num = p2Score % 100;
+    app->p2NumSt = (app->p2Num / 10) % 10;
+    app->p2NumNd = app->p2Num % 10;
+
+    SDL_UnlockMutex(app->mutex);
+    
+    app->updateRequire = 1;
+    return 0;
+}
+
+/*
+int main() {
+    struct Application myApp, *app = &myApp;
+    
+    if (initApplication(app)) {
+        printf("初始化应用失败: %s\n", app->errorString);
+        termApplication(app);
+        return 0;
+    }
+
+    if (app->initialized) {
+        updateApplication(app, "昵称最多有八个字", "昵称最多有八个字", 5, 6, 6);
+        runApplication(app);
+    }
+
+    termApplication(app);
+
+    return 0;
+}*/

+ 59 - 0
ui.h

@@ -0,0 +1,59 @@
+#ifndef UI_H_
+#define UI_H_
+
+#include <SDL.h>
+#include <SDL_mutex.h>
+#include <SDL_image.h>
+#include <SDL_ttf.h>
+
+typedef unsigned char uint8;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Application {
+    int ret;
+    char errorString[2048];
+
+    uint8 initialized;    
+
+    SDL_Window *window;
+    int winWidth, winHeight;
+
+    SDL_Renderer *renderer;
+    
+    SDL_Texture *bgTexture;
+    SDL_Texture *bigNumTextures[10];
+    SDL_Texture *smallNumTextures[10];
+
+    TTF_Font *nameFont;
+    SDL_Texture *p1NameTexture, *p2NameTexture;
+    SDL_Texture *p1WinTexture[2], *p2WinTexture[2];
+    
+    SDL_Texture *scoreHighLightTexture[2];
+    
+    char p1Name[30];
+    char p2Name[30];
+    uint8 roundNum, roundNumSt, roundNumNd;
+    uint8 p1Num, p1NumSt, p1NumNd;
+    uint8 p2Num, p2NumSt, p2NumNd;
+    uint8 p1Win, p2Win;
+
+    uint8 isKey;
+
+    uint8 updateRequire;
+    SDL_mutex *mutex;
+};
+
+int initApplication(struct Application *app); 
+void drawApplication(struct Application *app);
+void termApplication(struct Application *app);
+int updateApplicationMode(struct Application *app, uint8 isKey);
+int updateApplication(struct Application *app, const char *p1Name, const char *p2Name, int roundNum, int p1Score, int p2Score); 
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // UI_H_