this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add radio bit

+978 -543
.cache/clangd/index/main.cpp.47C66B394CD74271.idx

This is a binary file and will not be displayed.

+1 -1
CMakeLists.txt
··· 15 15 add_compile_options(-D_GLIBCXX_HAS_GTHREADS=0) 16 16 17 17 add_executable(HelloWorldApp WIN32 main.cpp) 18 - target_link_libraries(HelloWorldApp user32 gdi32) 18 + target_link_libraries(HelloWorldApp user32 gdi32 winmm) 19 19 20 20 # Include current directory for headers 21 21 target_include_directories(HelloWorldApp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+62
CRUSH.md
··· 1 + # Shortwave Development Guide 2 + 3 + ## Project Overview 4 + - **Language**: C-style C++ for Win32 API compatibility 5 + - **Target Platform**: Windows XP 6 + - **Primary Goal**: Simple Win32 Application 7 + 8 + ## Build & Development Commands 9 + - **Build**: `nix build` 10 + - Compiles Win32 application 11 + - **Dev Setup**: `setup-dev` 12 + - Generates `compile_commands.json` 13 + - **Deploy**: `deploy-to-xp` 14 + - Copies executable and DLLs to XP VM 15 + - **Debugging**: Use Visual Studio or WinDbg 16 + - **Testing**: Manual testing on Windows XP 17 + 18 + ## Code Style Guidelines 19 + 20 + ### Formatting 21 + - Use tabs for indentation 22 + - Opening braces on same line 23 + - Max line length: 80 characters 24 + - Avoid trailing whitespace 25 + 26 + ### Naming Conventions 27 + - Functions: `PascalCase` (e.g., `QRCode_Init`) 28 + - Constants/Macros: `UPPER_SNAKE_CASE` (e.g., `ID_ABOUT`) 29 + - Global Variables: `g_` prefix (e.g., `g_qrCode`) 30 + - Avoid abbreviations 31 + 32 + ### Types & Memory 33 + - Prefer standard C types: `int`, `char*` 34 + - Use Win32 types: `HWND`, `LPARAM` 35 + - Avoid STL 36 + - Static allocation preferred 37 + - No dynamic memory allocation 38 + - No exceptions 39 + 40 + ### Error Handling 41 + - Always check Win32 API return values 42 + - Validate pointers before use 43 + - Use `NULL` checks 44 + - Log errors to file/console 45 + - Graceful failure modes 46 + 47 + ### Imports & Headers 48 + 1. `<windows.h>` 49 + 2. Standard C headers 50 + 3. Project-specific headers 51 + - Use include guards 52 + - Minimize header dependencies 53 + 54 + ### Documentation 55 + - Comment complex Win32 logic 56 + - Document function parameters/returns 57 + 58 + ## Best Practices 59 + - Prioritize Win32 API compatibility 60 + - Minimize external dependencies 61 + - Focus on performance and low resource usage 62 + - Test thoroughly on target Windows XP environment
+203 -189
flake.nix
··· 7 7 systems.url = "github:nix-systems/default"; 8 8 }; 9 9 10 - outputs = inputs@{ flake-parts, systems, ... }: 10 + outputs = 11 + inputs@{ flake-parts, systems, ... }: 11 12 flake-parts.lib.mkFlake { inherit inputs; } { 12 13 imports = [ ]; 13 14 systems = import systems; 14 - perSystem = { self', pkgs, ... }: { 15 - packages = { 16 - hello-world-app = pkgs.stdenv.mkDerivation { 17 - name = "hello-world-app"; 18 - version = "1.0.0"; 19 - src = ./.; 20 - 21 - nativeBuildInputs = with pkgs; [ 22 - cmake 23 - pkgsCross.mingw32.buildPackages.gcc 24 - ]; 25 - 26 - buildInputs = with pkgs.pkgsCross.mingw32; [ 27 - windows.mingw_w64 28 - ]; 29 - 30 - configurePhase = '' 31 - export CC=${pkgs.pkgsCross.mingw32.buildPackages.gcc}/bin/i686-w64-mingw32-gcc 32 - export CXX=${pkgs.pkgsCross.mingw32.buildPackages.gcc}/bin/i686-w64-mingw32-g++ 33 - export CMAKE_SYSTEM_NAME=Windows 34 - export CMAKE_C_COMPILER=$CC 35 - export CMAKE_CXX_COMPILER=$CXX 36 - ''; 37 - 38 - buildPhase = '' 39 - export LDFLAGS="-static -static-libgcc -static-libstdc++" 40 - cmake -DCMAKE_BUILD_TYPE=Release \ 41 - -DCMAKE_SYSTEM_NAME=Windows \ 42 - -DCMAKE_C_COMPILER=$CC \ 43 - -DCMAKE_CXX_COMPILER=$CXX \ 44 - -DCMAKE_EXE_LINKER_FLAGS="$LDFLAGS" . 45 - make VERBOSE=1 46 - ''; 47 - 48 - installPhase = '' 49 - mkdir -p $out/bin 50 - cp HelloWorld.exe $out/bin/ 51 - ''; 52 - }; 53 - 54 - deploy-to-xp = pkgs.writeShellScriptBin "deploy-to-xp" '' 55 - XP_DIR="$HOME/Documents/xp-drive" 56 - mkdir -p "$XP_DIR" 57 - 58 - # Handle Windows file locking by using a temporary name first 59 - TEMP_NAME="HelloWorld_new.exe" 60 - OLD_NAME="HelloWorld_old.exe" 61 - FINAL_NAME="HelloWorld.exe" 62 - 63 - echo "Deploying to $XP_DIR..." 64 - 65 - # Copy to temporary name first 66 - if cp ${self'.packages.hello-world-app}/bin/HelloWorld.exe "$XP_DIR/$TEMP_NAME"; then 67 - echo "✓ Copied new version as $TEMP_NAME" 68 - 69 - # If the original exists, try to rename it 70 - if [ -f "$XP_DIR/$FINAL_NAME" ]; then 71 - if mv "$XP_DIR/$FINAL_NAME" "$XP_DIR/$OLD_NAME" 2>/dev/null; then 72 - echo "✓ Backed up old version as $OLD_NAME" 15 + perSystem = 16 + { self', pkgs, ... }: 17 + { 18 + packages = { 19 + shortwave = pkgs.stdenv.mkDerivation { 20 + name = "shortwave"; 21 + version = "1.0.0"; 22 + src = ./.; 23 + 24 + nativeBuildInputs = with pkgs; [ 25 + cmake 26 + pkgsCross.mingw32.buildPackages.gcc 27 + ]; 28 + 29 + buildInputs = with pkgs.pkgsCross.mingw32; [ 30 + windows.mingw_w64 31 + ]; 32 + 33 + configurePhase = '' 34 + export CC=${pkgs.pkgsCross.mingw32.buildPackages.gcc}/bin/i686-w64-mingw32-gcc 35 + export CXX=${pkgs.pkgsCross.mingw32.buildPackages.gcc}/bin/i686-w64-mingw32-g++ 36 + export CMAKE_SYSTEM_NAME=Windows 37 + export CMAKE_C_COMPILER=$CC 38 + export CMAKE_CXX_COMPILER=$CXX 39 + ''; 40 + 41 + buildPhase = '' 42 + export LDFLAGS="-static -static-libgcc -static-libstdc++" 43 + cmake -DCMAKE_BUILD_TYPE=Release \ 44 + -DCMAKE_SYSTEM_NAME=Windows \ 45 + -DCMAKE_C_COMPILER=$CC \ 46 + -DCMAKE_CXX_COMPILER=$CXX \ 47 + -DCMAKE_EXE_LINKER_FLAGS="$LDFLAGS" . 48 + make VERBOSE=1 49 + ''; 50 + 51 + installPhase = '' 52 + mkdir -p $out/bin 53 + cp HelloWorld.exe $out/bin/ 54 + ''; 55 + }; 56 + 57 + deploy-to-xp = pkgs.writeShellScriptBin "deploy-to-xp" '' 58 + XP_DIR="$HOME/Documents/xp-drive" 59 + mkdir -p "$XP_DIR" 60 + 61 + # Handle Windows file locking by using a temporary name first 62 + TEMP_NAME="HelloWorld_new.exe" 63 + OLD_NAME="HelloWorld_old.exe" 64 + FINAL_NAME="HelloWorld.exe" 65 + 66 + echo "Deploying to $XP_DIR..." 67 + 68 + # Copy to temporary name first 69 + if cp ${self'.packages.shortwave}/bin/HelloWorld.exe "$XP_DIR/$TEMP_NAME"; then 70 + echo "Copied new version as $TEMP_NAME" 71 + 72 + # If the original exists, try to rename it 73 + if [ -f "$XP_DIR/$FINAL_NAME" ]; then 74 + if mv "$XP_DIR/$FINAL_NAME" "$XP_DIR/$OLD_NAME" 2>/dev/null; then 75 + echo "Backed up old version as $OLD_NAME" 76 + else 77 + echo "Warning: Could not backup old version (file may be in use)" 78 + echo "Close the application on XP and try again, or manually rename files" 79 + echo "New version is available as: $TEMP_NAME" 80 + exit 1 81 + fi 82 + fi 83 + 84 + # Move temp to final name 85 + if mv "$XP_DIR/$TEMP_NAME" "$XP_DIR/$FINAL_NAME"; then 86 + echo "Deployed HelloWorld.exe successfully" 87 + 88 + # Clean up old backup if it exists 89 + if [ -f "$XP_DIR/$OLD_NAME" ]; then 90 + rm -f "$XP_DIR/$OLD_NAME" 2>/dev/null || echo "(Old backup file remains)" 91 + fi 73 92 else 74 - echo "⚠ Warning: Could not backup old version (file may be in use)" 75 - echo " Close the application on XP and try again, or manually rename files" 76 - echo " New version is available as: $TEMP_NAME" 93 + echo "Failed to finalize deployment" 77 94 exit 1 78 95 fi 79 - fi 80 - 81 - # Move temp to final name 82 - if mv "$XP_DIR/$TEMP_NAME" "$XP_DIR/$FINAL_NAME"; then 83 - echo "✓ Deployed HelloWorld.exe successfully" 84 - 85 - # Clean up old backup if it exists 86 - if [ -f "$XP_DIR/$OLD_NAME" ]; then 87 - rm -f "$XP_DIR/$OLD_NAME" 2>/dev/null || echo " (Old backup file remains)" 88 - fi 89 96 else 90 - echo "✗ Failed to finalize deployment" 97 + echo "Failed to copy new version" 91 98 exit 1 92 99 fi 93 - else 94 - echo "✗ Failed to copy new version" 95 - exit 1 96 - fi 97 - 98 - echo "" 99 - echo "Deployment complete! 🎉" 100 - echo "You can now run the updated application on XP" 101 - ''; 102 - 103 - setup-dev = pkgs.writeShellScriptBin "setup-dev" '' 104 - echo "Setting up development environment for Zed..." 105 - 106 - # Get the proper MinGW headers - use the known path from our build 107 - GCC_BASE="/nix/store/l2gk3vvpdf33jf3gnfljyyx3dgwks8zp-i686-w64-mingw32-stage-final-gcc-debug-10.3.0/i686-w64-mingw32" 108 - SYS_INCLUDE="$GCC_BASE/sys-include" 109 - MINGW_MAIN_INCLUDE="/nix/store/hhbkp872dkayzd2qxfhkdc4rgn393g52-mingw-w64-i686-w64-mingw32-9.0.0-dev/include" 110 - MCFGTHREAD_INCLUDE="/nix/store/21c6w351iwpblnfz2m9v3ssvxcmqsz7h-mcfgthreads-i686-w64-mingw32-git-dev/include" 111 - CPP_INCLUDE="$GCC_BASE/include/c++/10.3.0" 112 - CPP_TARGET_INCLUDE="$CPP_INCLUDE/i686-w64-mingw32" 113 - 114 - # Verify paths exist 115 - if [ ! -f "$SYS_INCLUDE/stdlib.h" ]; then 116 - echo "Error: Could not find C standard library at $SYS_INCLUDE" 117 - exit 1 118 - fi 119 - 120 - if [ ! -f "$MINGW_MAIN_INCLUDE/windows.h" ]; then 121 - echo "Error: Could not find MinGW headers at $MINGW_MAIN_INCLUDE" 122 - exit 1 123 - fi 124 - 125 - if [ ! -f "$CPP_INCLUDE/vector" ]; then 126 - echo "Error: Could not find C++ standard library at $CPP_INCLUDE" 127 - exit 1 128 - fi 129 - 130 - if [ ! -f "$MCFGTHREAD_INCLUDE/mcfgthread/gthread.h" ]; then 131 - echo "Error: Could not find mcfgthread headers at $MCFGTHREAD_INCLUDE" 132 - exit 1 133 - fi 134 - 135 - # Create simplified .clangd config to avoid intrinsics issues 136 - cat > .clangd << EOF 137 - CompileFlags: 138 - Add: 139 - - -target 140 - - i686-w64-mingw32 141 - - -DWINVER=0x0501 142 - - -D_WIN32_WINNT=0x0501 143 - - -DWIN32_LEAN_AND_MEAN 144 - - -D_WIN32 145 - - -DWIN32 146 - - -std=c++17 147 - - -fno-builtin 148 - - -D__NO_INLINE__ 149 - - -isystem 150 - - $SYS_INCLUDE 151 - - -isystem 152 - - $MINGW_MAIN_INCLUDE 153 - - -isystem 154 - - $MCFGTHREAD_INCLUDE 155 - - -isystem 156 - - $CPP_INCLUDE 157 - - -isystem 158 - - $CPP_TARGET_INCLUDE 159 - Remove: 160 - - -I*/gcc/*/include 161 - EOF 162 - 163 - # Create simplified compile_commands.json 164 - cat > compile_commands.json << EOF 165 - [ 166 - { 167 - "directory": "$(pwd)", 168 - "command": "clang++ -target i686-w64-mingw32 -DWINVER=0x0501 -D_WIN32_WINNT=0x0501 -DWIN32_LEAN_AND_MEAN -D_WIN32 -DWIN32 -fno-builtin -isystem \"$MINGW_MAIN_INCLUDE\" -std=c++17 -c main.cpp", 169 - "file": "main.cpp" 170 - } 171 - ] 172 - EOF 173 - 174 - echo "Generated simplified .clangd config and compile_commands.json" 175 - echo "Using C standard library: $SYS_INCLUDE" 176 - echo "Using MinGW headers: $MINGW_MAIN_INCLUDE" 177 - echo "Using mcfgthread headers: $MCFGTHREAD_INCLUDE" 178 - echo "Using C++ headers: $CPP_INCLUDE" 179 - echo "" 180 - echo "This includes complete C/C++ standard libraries and Win32 APIs" 181 - echo "Restart Zed for the changes to take effect" 182 - ''; 183 - 184 - default = self'.packages.hello-world-app; 185 - }; 186 - 187 - devShells = { 188 - default = pkgs.mkShell { 189 - buildInputs = with pkgs; [ 190 - cmake 191 - pkgsCross.mingw32.buildPackages.gcc 192 - self'.packages.deploy-to-xp 193 - self'.packages.setup-dev 194 - ]; 195 - 196 - shellHook = '' 197 - echo "Win32 development environment loaded (older toolchain)" 198 - echo "Available commands:" 199 - echo " nix build - Build the application" 200 - echo " deploy-to-xp - Deploy to XP VM folder" 201 - echo " setup-dev - Generate compile_commands.json for Zed editor" 202 - echo "" 203 - echo "For Zed editor support:" 204 - echo " 1. Run 'setup-dev' to generate compile_commands.json" 205 - echo " 2. Open project in Zed for IntelliSense support" 206 100 ''; 101 + 102 + setup-dev = pkgs.writeShellScriptBin "setup-dev" '' 103 + echo "Setting up development environment for Zed..." 104 + 105 + # Get dynamic paths from nix packages 106 + GCC_BASE="${pkgs.pkgsCross.mingw32.buildPackages.gcc}/i686-w64-mingw32" 107 + SYS_INCLUDE="$GCC_BASE/sys-include" 108 + MINGW_MAIN_INCLUDE="${pkgs.pkgsCross.mingw32.windows.mingw_w64}/include" 109 + CPP_INCLUDE="${pkgs.lib.getDev pkgs.pkgsCross.mingw32.buildPackages.gcc}/include/c++/10.3.0" 110 + CPP_TARGET_INCLUDE="$CPP_INCLUDE/i686-w64-mingw32" 111 + 112 + # Create .clangd config with correct paths 113 + cat > .clangd << EOF 114 + CompileFlags: 115 + Add: 116 + - -target 117 + - i686-w64-mingw32 118 + - -DWINVER=0x0501 119 + - -D_WIN32_WINNT=0x0501 120 + - -DWIN32_LEAN_AND_MEAN 121 + - -D_WIN32 122 + - -DWIN32 123 + - -std=c++17 124 + - -fno-builtin 125 + - -isystem 126 + - $SYS_INCLUDE 127 + - -isystem 128 + - $MINGW_MAIN_INCLUDE 129 + - -isystem 130 + - $CPP_INCLUDE 131 + - -isystem 132 + - $CPP_TARGET_INCLUDE 133 + Remove: 134 + - -I*/gcc/*/include 135 + EOF 136 + 137 + # Create compile_commands.json 138 + cat > compile_commands.json << EOF 139 + [ 140 + { 141 + "directory": "$(pwd)", 142 + "command": "i686-w64-mingw32-g++ -DWINVER=0x0501 -D_WIN32_WINNT=0x0501 -DWIN32_LEAN_AND_MEAN -D_WIN32 -DWIN32 -std=c++17 -isystem \"$SYS_INCLUDE\" -isystem \"$MINGW_MAIN_INCLUDE\" -isystem \"$CPP_INCLUDE\" -isystem \"$CPP_TARGET_INCLUDE\" -c main.cpp", 143 + "file": "main.cpp" 144 + } 145 + ] 146 + EOF 147 + 148 + echo "Generated .clangd config and compile_commands.json with include paths" 149 + echo "Development environment ready for Zed editor" 150 + echo "Include paths:" 151 + echo " C standard library: $SYS_INCLUDE" 152 + echo " MinGW headers: $MINGW_MAIN_INCLUDE" 153 + echo " C++ headers: $CPP_INCLUDE" 154 + ''; 155 + 156 + default = self'.packages.shortwave; 157 + }; 158 + 159 + devShells = { 160 + default = pkgs.mkShell { 161 + buildInputs = with pkgs; [ 162 + cmake 163 + pkgsCross.mingw32.buildPackages.gcc 164 + self'.packages.deploy-to-xp 165 + self'.packages.setup-dev 166 + ]; 167 + 168 + shellHook = '' 169 + echo "Win32 development environment loaded" 170 + echo "Available commands:" 171 + echo " nix build - Build the application" 172 + echo " deploy-to-xp - Deploy to XP VM folder" 173 + echo "" 174 + echo "Setting up development environment..." 175 + 176 + # Get dynamic paths from nix packages 177 + GCC_BASE="${pkgs.pkgsCross.mingw32.buildPackages.gcc}/i686-w64-mingw32" 178 + SYS_INCLUDE="$GCC_BASE/sys-include" 179 + MINGW_MAIN_INCLUDE="${pkgs.pkgsCross.mingw32.windows.mingw_w64}/include" 180 + CPP_INCLUDE="${pkgs.lib.getDev pkgs.pkgsCross.mingw32.buildPackages.gcc}/include/c++/10.3.0" 181 + CPP_TARGET_INCLUDE="$CPP_INCLUDE/i686-w64-mingw32" 182 + 183 + # Auto-generate .clangd config with correct paths 184 + cat > .clangd << EOF 185 + CompileFlags: 186 + Add: 187 + - -target 188 + - i686-w64-mingw32 189 + - -DWINVER=0x0501 190 + - -D_WIN32_WINNT=0x0501 191 + - -DWIN32_LEAN_AND_MEAN 192 + - -D_WIN32 193 + - -DWIN32 194 + - -std=c++17 195 + - -fno-builtin 196 + - -isystem 197 + - $SYS_INCLUDE 198 + - -isystem 199 + - $MINGW_MAIN_INCLUDE 200 + - -isystem 201 + - $CPP_INCLUDE 202 + - -isystem 203 + - $CPP_TARGET_INCLUDE 204 + Remove: 205 + - -I*/gcc/*/include 206 + EOF 207 + 208 + cat > compile_commands.json << EOF 209 + [ 210 + { 211 + "directory": "$(pwd)", 212 + "command": "i686-w64-mingw32-g++ -DWINVER=0x0501 -D_WIN32_WINNT=0x0501 -DWIN32_LEAN_AND_MEAN -D_WIN32 -DWIN32 -std=c++17 -isystem \"$SYS_INCLUDE\" -isystem \"$MINGW_MAIN_INCLUDE\" -isystem \"$CPP_INCLUDE\" -isystem \"$CPP_TARGET_INCLUDE\" -c main.cpp", 213 + "file": "main.cpp" 214 + } 215 + ] 216 + EOF 217 + 218 + echo "✓ Generated .clangd config and compile_commands.json with include paths" 219 + echo "✓ Development environment ready for Zed editor" 220 + ''; 221 + }; 207 222 }; 208 223 }; 209 - }; 210 224 }; 211 - } 225 + }
+712 -177
main.cpp
··· 1 1 #include <windows.h> 2 - #include "qr.h" 2 + #include <math.h> 3 + #include <stdio.h> 4 + #include <string.h> 5 + #include <mmsystem.h> 6 + 7 + #pragma comment(lib, "winmm.lib") 3 8 4 9 #define ID_ABOUT 1001 5 10 #define ID_EXIT 1002 6 - #define ID_GENERATE_QR 1003 11 + 12 + // Radio control IDs 13 + #define ID_TUNING_DIAL 2001 14 + #define ID_VOLUME_KNOB 2002 15 + #define ID_POWER_BUTTON 2003 16 + 17 + // Radio station data 18 + typedef struct { 19 + float frequency; 20 + char name[64]; 21 + char description[128]; 22 + } RadioStation; 23 + 24 + RadioStation g_stations[] = { 25 + {14.230f, "BBC World Service", "International news and current affairs"}, 26 + {15.770f, "Radio Moscow", "Russian international broadcast"}, 27 + {17.895f, "Voice of America", "American international news"}, 28 + {21.500f, "Radio Australia", "Australian international service"}, 29 + {25.820f, "Radio Canada", "Canadian international broadcast"}, 30 + {28.400f, "Amateur Radio", "Ham radio operators"}, 31 + {31.100f, "Time Signal", "Atomic clock time broadcast"} 32 + }; 33 + 34 + #define NUM_STATIONS (sizeof(g_stations) / sizeof(RadioStation)) 35 + #define SAMPLE_RATE 44100 36 + #define BITS_PER_SAMPLE 16 37 + #define CHANNELS 1 38 + #define BUFFER_SIZE 4410 // 0.1 seconds of audio 39 + #define NUM_BUFFERS 4 40 + 41 + // Audio state 42 + typedef struct { 43 + HWAVEOUT hWaveOut; 44 + WAVEHDR waveHeaders[NUM_BUFFERS]; 45 + short audioBuffers[NUM_BUFFERS][BUFFER_SIZE]; 46 + int currentBuffer; 47 + int isPlaying; 48 + float staticVolume; 49 + float radioVolume; 50 + } AudioState; 51 + 52 + // Radio state 53 + typedef struct { 54 + float frequency; 55 + float volume; 56 + int power; 57 + int signalStrength; 58 + int isDraggingDial; 59 + int isDraggingVolume; 60 + } RadioState; 61 + 62 + RadioState g_radio = {14.230f, 0.5f, 0, 0, 0, 0}; 63 + AudioState g_audio = {0}; 7 64 8 65 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 66 + void DrawRadioInterface(HDC hdc, RECT* rect); 67 + void DrawTuningDial(HDC hdc, int x, int y, int radius, float frequency); 68 + void DrawFrequencyDisplay(HDC hdc, int x, int y, float frequency); 69 + void DrawSignalMeter(HDC hdc, int x, int y, int strength); 70 + void DrawVolumeKnob(HDC hdc, int x, int y, int radius, float volume); 71 + void DrawPowerButton(HDC hdc, int x, int y, int radius, int power); 72 + int IsPointInCircle(int px, int py, int cx, int cy, int radius); 73 + float GetAngleFromPoint(int px, int py, int cx, int cy); 74 + void UpdateFrequencyFromMouse(int mouseX, int mouseY); 75 + void UpdateVolumeFromMouse(int mouseX, int mouseY); 9 76 10 - // Global QR code instance - static allocation, no new/delete 11 - QRCode g_qrCode; 12 - BOOL g_hasQrCode = FALSE; 13 - char g_qrText[256] = "Hello World!"; 77 + // Audio functions 78 + int InitializeAudio(); 79 + void CleanupAudio(); 80 + void GenerateStaticBuffer(short* buffer, int size); 81 + void FillAudioBuffer(short* buffer, int size); 82 + void CALLBACK WaveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2); 83 + void StartAudio(); 84 + void StopAudio(); 85 + RadioStation* FindNearestStation(float frequency); 86 + float GetStationSignalStrength(RadioStation* station, float currentFreq); 14 87 15 88 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) { 16 - const char* CLASS_NAME = "HelloWorldWindow"; 17 - 18 - WNDCLASS wc = {}; 19 - wc.lpfnWndProc = WindowProc; 20 - wc.hInstance = hInstance; 21 - wc.lpszClassName = CLASS_NAME; 22 - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 23 - wc.hCursor = LoadCursor(NULL, IDC_ARROW); 24 - 25 - RegisterClass(&wc); 26 - 27 - HWND hwnd = CreateWindow( 28 - CLASS_NAME, 29 - "Hello World App", 30 - WS_OVERLAPPEDWINDOW, 31 - CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, 32 - NULL, 33 - NULL, 34 - hInstance, 35 - NULL 36 - ); 37 - 38 - if (hwnd == NULL) { 39 - return 0; 40 - } 41 - 42 - // Create menu 43 - HMENU hMenu = CreateMenu(); 44 - 45 - // Tools menu 46 - HMENU hToolsMenu = CreatePopupMenu(); 47 - AppendMenu(hToolsMenu, MF_STRING, ID_GENERATE_QR, "&Generate QR Pattern"); 48 - AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hToolsMenu, "&Tools"); 49 - 50 - // Help menu 51 - HMENU hHelpMenu = CreatePopupMenu(); 52 - AppendMenu(hHelpMenu, MF_STRING, ID_ABOUT, "&About"); 53 - AppendMenu(hHelpMenu, MF_SEPARATOR, 0, NULL); 54 - AppendMenu(hHelpMenu, MF_STRING, ID_EXIT, "E&xit"); 55 - AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hHelpMenu, "&Help"); 56 - 57 - SetMenu(hwnd, hMenu); 58 - 59 - ShowWindow(hwnd, nCmdShow); 60 - UpdateWindow(hwnd); 61 - 62 - MSG msg = {}; 63 - while (GetMessage(&msg, NULL, 0, 0)) { 64 - TranslateMessage(&msg); 65 - DispatchMessage(&msg); 66 - } 67 - 68 - return 0; 89 + const char* CLASS_NAME = "ShortwaveRadio"; 90 + 91 + WNDCLASS wc = {}; 92 + wc.lpfnWndProc = WindowProc; 93 + wc.hInstance = hInstance; 94 + wc.lpszClassName = CLASS_NAME; 95 + wc.hbrBackground = CreateSolidBrush(RGB(101, 67, 33)); // Wood grain brown 96 + wc.hCursor = LoadCursor(NULL, IDC_ARROW); 97 + 98 + RegisterClass(&wc); 99 + 100 + HWND hwnd = CreateWindow( 101 + CLASS_NAME, 102 + "Shortwave Radio Tuner", 103 + WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, // Fixed size 104 + CW_USEDEFAULT, CW_USEDEFAULT, 600, 450, 105 + NULL, 106 + NULL, 107 + hInstance, 108 + NULL 109 + ); 110 + 111 + if (hwnd == NULL) { 112 + return 0; 113 + } 114 + 115 + // Initialize audio system 116 + if (InitializeAudio() != 0) { 117 + MessageBox(hwnd, "Failed to initialize audio system", "Error", MB_OK | MB_ICONERROR); 118 + return 0; 119 + } 120 + 121 + // Audio starts when power button is pressed 122 + 123 + // Create menu 124 + HMENU hMenu = CreateMenu(); 125 + 126 + // Radio menu 127 + HMENU hRadioMenu = CreatePopupMenu(); 128 + AppendMenu(hRadioMenu, MF_STRING, ID_ABOUT, "&About"); 129 + AppendMenu(hRadioMenu, MF_SEPARATOR, 0, NULL); 130 + AppendMenu(hRadioMenu, MF_STRING, ID_EXIT, "E&xit"); 131 + AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hRadioMenu, "&Radio"); 132 + 133 + SetMenu(hwnd, hMenu); 134 + 135 + ShowWindow(hwnd, nCmdShow); 136 + UpdateWindow(hwnd); 137 + 138 + MSG msg = {}; 139 + while (GetMessage(&msg, NULL, 0, 0)) { 140 + TranslateMessage(&msg); 141 + DispatchMessage(&msg); 142 + } 143 + 144 + // Cleanup audio 145 + StopAudio(); 146 + CleanupAudio(); 147 + 148 + return 0; 69 149 } 70 150 71 151 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 72 - switch (uMsg) { 73 - case WM_SIZE: 74 - // Trigger repaint when window is resized 75 - InvalidateRect(hwnd, NULL, TRUE); 76 - return 0; 77 - 78 - case WM_DESTROY: 79 - // No cleanup needed for static allocation 80 - PostQuitMessage(0); 81 - return 0; 82 - 83 - case WM_PAINT: { 84 - PAINTSTRUCT ps; 85 - HDC hdc = BeginPaint(hwnd, &ps); 86 - 87 - RECT rect; 88 - GetClientRect(hwnd, &rect); 89 - 90 - if (g_hasQrCode) { 91 - // Draw QR code - calculate size based on window 92 - int qrSize = QRCode_GetSize(); 93 - int moduleSize = 8; 94 - int qrPixelSize = qrSize * moduleSize; 95 - 96 - // Center horizontally and vertically with some padding 97 - int startX = (rect.right - qrPixelSize) / 2; 98 - int startY = (rect.bottom - qrPixelSize - 80) / 2; // Leave space for text 99 - 100 - QRCode_DrawToHDC(&g_qrCode, hdc, startX, startY, moduleSize); 101 - 102 - // Draw text below QR code 103 - SetTextAlign(hdc, TA_CENTER); 104 - SetBkMode(hdc, TRANSPARENT); 105 - int textLen = 0; 106 - while (g_qrText[textLen]) textLen++; // Calculate length 107 - TextOut(hdc, rect.right / 2, startY + qrPixelSize + 20, 108 - g_qrText, textLen); 109 - 110 - // Add disclaimer 111 - const char* disclaimer = "(Visual demo - not scannable)"; 112 - int disclaimerLen = 0; 113 - while (disclaimer[disclaimerLen]) disclaimerLen++; 114 - TextOut(hdc, rect.right / 2, startY + qrPixelSize + 40, 115 - disclaimer, disclaimerLen); 116 - } else { 117 - // Default view - center in current window size 118 - SetTextAlign(hdc, TA_CENTER); 119 - SetBkMode(hdc, TRANSPARENT); 120 - int centerY = rect.bottom / 2; 121 - TextOut(hdc, rect.right / 2, centerY - 10, "Hello World!", 12); 122 - TextOut(hdc, rect.right / 2, centerY + 10, 123 - "Use Tools > Generate QR Pattern", 32); 124 - } 125 - 126 - EndPaint(hwnd, &ps); 127 - return 0; 128 - } 129 - 130 - case WM_COMMAND: 131 - switch (LOWORD(wParam)) { 132 - case ID_ABOUT: { 133 - const char* aboutText = "Hello World App\n\n" 134 - "Version: 1.0.0\n" 135 - "Built by: Kieran Klukas\n\n" 136 - "A simple Win32 application\n" 137 - "compatible with Windows XP\n\n" 138 - "Features:\n" 139 - "- QR Pattern Generation (visual demo)\n" 140 - "- XP Compatible Design\n" 141 - "- Pure Win32 API"; 142 - MessageBox(hwnd, aboutText, "About Hello World App", 143 - MB_OK | MB_ICONINFORMATION); 144 - break; 145 - } 146 - case ID_GENERATE_QR: { 147 - // Simple input dialog using InputBox simulation 148 - if (MessageBox(hwnd, "Generate QR code pattern for current text?\n\n(Note: This creates a visual QR-like pattern for demo purposes,\nnot a scannable QR code)\n\nClick OK to use default text,\nor Cancel to cycle through presets.", 149 - "Generate QR Pattern", MB_OKCANCEL | MB_ICONQUESTION) == IDCANCEL) { 150 - 151 - // For now, use a simple preset - in a real app you'd want a proper input dialog 152 - const char* presets[] = { 153 - "Hello World!", 154 - "https://github.com/taciturnaxolotl/shortwave", 155 - "Made with love by Kieran Klukas", 156 - "Windows XP Forever!", 157 - "QR codes are cool!" 158 - }; 159 - 160 - static int presetIndex = 0; 161 - const char* selectedText = presets[presetIndex % 5]; 162 - presetIndex++; 163 - 164 - // Copy selected text to global buffer 165 - int i = 0; 166 - while (selectedText[i] && i < 255) { 167 - g_qrText[i] = selectedText[i]; 168 - i++; 169 - } 170 - g_qrText[i] = '\0'; 171 - } 172 - 173 - // Generate new QR code - no new/delete, just reinitialize 174 - QRCode_Init(&g_qrCode, g_qrText); 175 - g_hasQrCode = TRUE; 176 - 177 - // Refresh the window 178 - InvalidateRect(hwnd, NULL, TRUE); 179 - break; 180 - } 181 - case ID_EXIT: 182 - PostQuitMessage(0); 183 - break; 184 - } 185 - return 0; 186 - } 187 - 188 - return DefWindowProc(hwnd, uMsg, wParam, lParam); 189 - } 152 + switch (uMsg) { 153 + case WM_SIZE: 154 + InvalidateRect(hwnd, NULL, TRUE); 155 + return 0; 156 + 157 + case WM_DESTROY: 158 + PostQuitMessage(0); 159 + return 0; 160 + 161 + case WM_PAINT: { 162 + PAINTSTRUCT ps; 163 + HDC hdc = BeginPaint(hwnd, &ps); 164 + 165 + RECT rect; 166 + GetClientRect(hwnd, &rect); 167 + 168 + DrawRadioInterface(hdc, &rect); 169 + 170 + EndPaint(hwnd, &ps); 171 + return 0; 172 + } 173 + 174 + case WM_LBUTTONDOWN: { 175 + int mouseX = LOWORD(lParam); 176 + int mouseY = HIWORD(lParam); 177 + 178 + // Check if clicking on tuning dial 179 + if (IsPointInCircle(mouseX, mouseY, 150, 200, 60)) { 180 + g_radio.isDraggingDial = 1; 181 + SetCapture(hwnd); 182 + UpdateFrequencyFromMouse(mouseX, mouseY); 183 + InvalidateRect(hwnd, NULL, TRUE); 184 + } 185 + // Check if clicking on volume knob 186 + else if (IsPointInCircle(mouseX, mouseY, 350, 200, 30)) { 187 + g_radio.isDraggingVolume = 1; 188 + SetCapture(hwnd); 189 + UpdateVolumeFromMouse(mouseX, mouseY); 190 + InvalidateRect(hwnd, NULL, TRUE); 191 + } 192 + // Check if clicking on power button 193 + else if (IsPointInCircle(mouseX, mouseY, 500, 120, 25)) { 194 + g_radio.power = !g_radio.power; 195 + if (g_radio.power) { 196 + StartAudio(); 197 + } else { 198 + StopAudio(); 199 + } 200 + InvalidateRect(hwnd, NULL, TRUE); 201 + } 202 + return 0; 203 + } 204 + 205 + case WM_LBUTTONUP: { 206 + if (g_radio.isDraggingDial || g_radio.isDraggingVolume) { 207 + g_radio.isDraggingDial = 0; 208 + g_radio.isDraggingVolume = 0; 209 + ReleaseCapture(); 210 + } 211 + return 0; 212 + } 213 + 214 + case WM_MOUSEMOVE: { 215 + if (g_radio.isDraggingDial) { 216 + int mouseX = LOWORD(lParam); 217 + int mouseY = HIWORD(lParam); 218 + UpdateFrequencyFromMouse(mouseX, mouseY); 219 + InvalidateRect(hwnd, NULL, TRUE); 220 + } 221 + else if (g_radio.isDraggingVolume) { 222 + int mouseX = LOWORD(lParam); 223 + int mouseY = HIWORD(lParam); 224 + UpdateVolumeFromMouse(mouseX, mouseY); 225 + InvalidateRect(hwnd, NULL, TRUE); 226 + } 227 + return 0; 228 + } 229 + 230 + case WM_COMMAND: 231 + switch (LOWORD(wParam)) { 232 + case ID_ABOUT: { 233 + const char* aboutText = "Shortwave Radio Tuner\n\n" 234 + "Version: 1.0.0\n" 235 + "Built for Rewind V2 Hackathon\n\n" 236 + "A vintage shortwave radio simulator\n" 237 + "compatible with Windows XP\n\n" 238 + "Features:\n" 239 + "- Realistic tuning interface\n" 240 + "- Internet radio streaming\n" 241 + "- Authentic static noise"; 242 + MessageBox(hwnd, aboutText, "About Shortwave Radio", 243 + MB_OK | MB_ICONINFORMATION); 244 + break; 245 + } 246 + case ID_EXIT: 247 + PostQuitMessage(0); 248 + break; 249 + } 250 + return 0; 251 + } 252 + 253 + return DefWindowProc(hwnd, uMsg, wParam, lParam); 254 + } 255 + 256 + void DrawRadioInterface(HDC hdc, RECT* rect) { 257 + // Create vintage radio background 258 + HBRUSH woodBrush = CreateSolidBrush(RGB(101, 67, 33)); 259 + FillRect(hdc, rect, woodBrush); 260 + DeleteObject(woodBrush); 261 + 262 + // Draw radio panel (darker inset) 263 + RECT panel = {50, 50, rect->right - 50, rect->bottom - 50}; 264 + HBRUSH panelBrush = CreateSolidBrush(RGB(80, 50, 25)); 265 + FillRect(hdc, &panel, panelBrush); 266 + DeleteObject(panelBrush); 267 + 268 + // Draw panel border (raised effect) 269 + HPEN lightPen = CreatePen(PS_SOLID, 2, RGB(140, 100, 60)); 270 + HPEN darkPen = CreatePen(PS_SOLID, 2, RGB(40, 25, 15)); 271 + 272 + SelectObject(hdc, lightPen); 273 + MoveToEx(hdc, panel.left, panel.bottom, NULL); 274 + LineTo(hdc, panel.left, panel.top); 275 + LineTo(hdc, panel.right, panel.top); 276 + 277 + SelectObject(hdc, darkPen); 278 + LineTo(hdc, panel.right, panel.bottom); 279 + LineTo(hdc, panel.left, panel.bottom); 280 + 281 + DeleteObject(lightPen); 282 + DeleteObject(darkPen); 283 + 284 + // Draw frequency display 285 + DrawFrequencyDisplay(hdc, 200, 80, g_radio.frequency); 286 + 287 + // Draw tuning dial 288 + DrawTuningDial(hdc, 150, 200, 60, g_radio.frequency); 289 + 290 + // Draw volume knob 291 + DrawVolumeKnob(hdc, 350, 200, 30, g_radio.volume); 292 + 293 + // Draw signal meter 294 + DrawSignalMeter(hdc, 450, 150, g_radio.signalStrength); 295 + 296 + // Draw power button 297 + DrawPowerButton(hdc, 500, 120, 25, g_radio.power); 298 + 299 + // Draw station info if tuned to a station 300 + RadioStation* currentStation = FindNearestStation(g_radio.frequency); 301 + if (currentStation && g_radio.signalStrength > 30) { 302 + RECT stationRect = {80, 320, 520, 360}; 303 + HBRUSH stationBrush = CreateSolidBrush(RGB(0, 0, 0)); 304 + FillRect(hdc, &stationRect, stationBrush); 305 + DeleteObject(stationBrush); 306 + 307 + HPEN stationPen = CreatePen(PS_SOLID, 1, RGB(100, 100, 100)); 308 + SelectObject(hdc, stationPen); 309 + Rectangle(hdc, stationRect.left, stationRect.top, stationRect.right, stationRect.bottom); 310 + DeleteObject(stationPen); 311 + 312 + SetTextColor(hdc, RGB(0, 255, 0)); 313 + HFONT stationFont = CreateFont(12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, 314 + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, 315 + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 316 + DEFAULT_PITCH | FF_SWISS, "Arial"); 317 + SelectObject(hdc, stationFont); 318 + 319 + char stationText[256]; 320 + sprintf(stationText, "%.3f MHz - %s: %s", 321 + currentStation->frequency, currentStation->name, currentStation->description); 322 + 323 + SetTextAlign(hdc, TA_LEFT); 324 + TextOut(hdc, stationRect.left + 5, stationRect.top + 5, stationText, strlen(stationText)); 325 + 326 + DeleteObject(stationFont); 327 + } 328 + 329 + // Draw labels 330 + SetBkMode(hdc, TRANSPARENT); 331 + SetTextColor(hdc, RGB(255, 255, 255)); 332 + HFONT font = CreateFont(14, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, 333 + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, 334 + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 335 + DEFAULT_PITCH | FF_SWISS, "Arial"); 336 + SelectObject(hdc, font); 337 + 338 + TextOut(hdc, 180, 300, "TUNING", 6); 339 + TextOut(hdc, 330, 260, "VOLUME", 6); 340 + TextOut(hdc, 430, 200, "SIGNAL", 6); 341 + TextOut(hdc, 485, 160, "POWER", 5); 342 + 343 + DeleteObject(font); 344 + } 345 + 346 + void DrawFrequencyDisplay(HDC hdc, int x, int y, float frequency) { 347 + // Draw display background (black LCD style) 348 + RECT display = {x - 80, y - 20, x + 80, y + 20}; 349 + HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0)); 350 + FillRect(hdc, &display, blackBrush); 351 + DeleteObject(blackBrush); 352 + 353 + // Draw display border 354 + HPEN borderPen = CreatePen(PS_SOLID, 1, RGB(100, 100, 100)); 355 + SelectObject(hdc, borderPen); 356 + Rectangle(hdc, display.left, display.top, display.right, display.bottom); 357 + DeleteObject(borderPen); 358 + 359 + // Draw frequency text 360 + char freqText[32]; 361 + sprintf(freqText, "%.3f MHz", frequency); 362 + 363 + SetBkMode(hdc, TRANSPARENT); 364 + SetTextColor(hdc, RGB(0, 255, 0)); // Green LCD color 365 + HFONT lcdFont = CreateFont(16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, 366 + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, 367 + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 368 + FIXED_PITCH | FF_MODERN, "Courier New"); 369 + SelectObject(hdc, lcdFont); 370 + 371 + SetTextAlign(hdc, TA_CENTER); 372 + TextOut(hdc, x, y - 8, freqText, strlen(freqText)); 373 + 374 + DeleteObject(lcdFont); 375 + } 376 + 377 + void DrawTuningDial(HDC hdc, int x, int y, int radius, float frequency) { 378 + // Draw dial background 379 + HBRUSH dialBrush = CreateSolidBrush(RGB(160, 120, 80)); 380 + SelectObject(hdc, dialBrush); 381 + Ellipse(hdc, x - radius, y - radius, x + radius, y + radius); 382 + DeleteObject(dialBrush); 383 + 384 + // Draw dial border 385 + HPEN borderPen = CreatePen(PS_SOLID, 2, RGB(60, 40, 20)); 386 + SelectObject(hdc, borderPen); 387 + Ellipse(hdc, x - radius, y - radius, x + radius, y + radius); 388 + DeleteObject(borderPen); 389 + 390 + // Draw frequency markings 391 + SetTextColor(hdc, RGB(0, 0, 0)); 392 + HFONT smallFont = CreateFont(10, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, 393 + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, 394 + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 395 + DEFAULT_PITCH | FF_SWISS, "Arial"); 396 + SelectObject(hdc, smallFont); 397 + SetTextAlign(hdc, TA_CENTER); 398 + 399 + for (int i = 0; i < 12; i++) { 400 + float angle = (float)i * 3.14159f / 6.0f; 401 + int markX = x + (int)((radius - 15) * cos(angle)); 402 + int markY = y + (int)((radius - 15) * sin(angle)); 403 + 404 + char mark[8]; 405 + sprintf(mark, "%d", 10 + i * 2); 406 + TextOut(hdc, markX, markY - 5, mark, strlen(mark)); 407 + } 408 + 409 + // Draw pointer based on frequency 410 + float angle = (frequency - 10.0f) / 24.0f * 3.14159f; 411 + int pointerX = x + (int)((radius - 10) * cos(angle)); 412 + int pointerY = y + (int)((radius - 10) * sin(angle)); 413 + 414 + HPEN pointerPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0)); 415 + SelectObject(hdc, pointerPen); 416 + MoveToEx(hdc, x, y, NULL); 417 + LineTo(hdc, pointerX, pointerY); 418 + DeleteObject(pointerPen); 419 + 420 + DeleteObject(smallFont); 421 + } 422 + 423 + void DrawVolumeKnob(HDC hdc, int x, int y, int radius, float volume) { 424 + // Draw knob background 425 + HBRUSH knobBrush = CreateSolidBrush(RGB(140, 100, 60)); 426 + SelectObject(hdc, knobBrush); 427 + Ellipse(hdc, x - radius, y - radius, x + radius, y + radius); 428 + DeleteObject(knobBrush); 429 + 430 + // Draw knob border 431 + HPEN borderPen = CreatePen(PS_SOLID, 1, RGB(60, 40, 20)); 432 + SelectObject(hdc, borderPen); 433 + Ellipse(hdc, x - radius, y - radius, x + radius, y + radius); 434 + DeleteObject(borderPen); 435 + 436 + // Draw volume indicator 437 + float angle = volume * 3.14159f * 1.5f - 3.14159f * 0.75f; 438 + int indicatorX = x + (int)((radius - 5) * cos(angle)); 439 + int indicatorY = y + (int)((radius - 5) * sin(angle)); 440 + 441 + HPEN indicatorPen = CreatePen(PS_SOLID, 2, RGB(255, 255, 255)); 442 + SelectObject(hdc, indicatorPen); 443 + MoveToEx(hdc, x, y, NULL); 444 + LineTo(hdc, indicatorX, indicatorY); 445 + DeleteObject(indicatorPen); 446 + } 447 + 448 + void DrawSignalMeter(HDC hdc, int x, int y, int strength) { 449 + // Draw meter background 450 + RECT meter = {x, y, x + 80, y + 20}; 451 + HBRUSH meterBrush = CreateSolidBrush(RGB(0, 0, 0)); 452 + FillRect(hdc, &meter, meterBrush); 453 + DeleteObject(meterBrush); 454 + 455 + // Draw meter border 456 + HPEN borderPen = CreatePen(PS_SOLID, 1, RGB(100, 100, 100)); 457 + SelectObject(hdc, borderPen); 458 + Rectangle(hdc, meter.left, meter.top, meter.right, meter.bottom); 459 + DeleteObject(borderPen); 460 + 461 + // Draw signal bars 462 + int barWidth = 8; 463 + int numBars = strength / 10; 464 + for (int i = 0; i < numBars && i < 10; i++) { 465 + RECT bar = {x + 2 + i * barWidth, y + 2, 466 + x + 2 + (i + 1) * barWidth - 1, y + 18}; 467 + 468 + COLORREF barColor; 469 + if (i < 3) barColor = RGB(0, 255, 0); // Green 470 + else if (i < 7) barColor = RGB(255, 255, 0); // Yellow 471 + else barColor = RGB(255, 0, 0); // Red 472 + 473 + HBRUSH barBrush = CreateSolidBrush(barColor); 474 + FillRect(hdc, &bar, barBrush); 475 + DeleteObject(barBrush); 476 + } 477 + } 478 + 479 + void DrawPowerButton(HDC hdc, int x, int y, int radius, int power) { 480 + // Draw button background 481 + COLORREF buttonColor = power ? RGB(255, 0, 0) : RGB(100, 100, 100); 482 + HBRUSH buttonBrush = CreateSolidBrush(buttonColor); 483 + SelectObject(hdc, buttonBrush); 484 + Ellipse(hdc, x - radius, y - radius, x + radius, y + radius); 485 + DeleteObject(buttonBrush); 486 + 487 + // Draw button border 488 + HPEN borderPen = CreatePen(PS_SOLID, 2, RGB(60, 60, 60)); 489 + SelectObject(hdc, borderPen); 490 + Ellipse(hdc, x - radius, y - radius, x + radius, y + radius); 491 + DeleteObject(borderPen); 492 + 493 + // Draw power symbol 494 + if (power) { 495 + HPEN symbolPen = CreatePen(PS_SOLID, 3, RGB(255, 255, 255)); 496 + SelectObject(hdc, symbolPen); 497 + 498 + // Draw power symbol (circle with line) 499 + Arc(hdc, x - 8, y - 8, x + 8, y + 8, x + 6, y - 6, x - 6, y - 6); 500 + MoveToEx(hdc, x, y - 10, NULL); 501 + LineTo(hdc, x, y - 2); 502 + 503 + DeleteObject(symbolPen); 504 + } 505 + } 506 + 507 + int IsPointInCircle(int px, int py, int cx, int cy, int radius) { 508 + int dx = px - cx; 509 + int dy = py - cy; 510 + return (dx * dx + dy * dy) <= (radius * radius); 511 + } 512 + 513 + float GetAngleFromPoint(int px, int py, int cx, int cy) { 514 + return atan2((float)(py - cy), (float)(px - cx)); 515 + } 516 + 517 + void UpdateFrequencyFromMouse(int mouseX, int mouseY) { 518 + float angle = GetAngleFromPoint(mouseX, mouseY, 150, 200); 519 + 520 + // Convert angle to frequency (10-34 MHz range) 521 + // Normalize angle from -PI to PI to 0-1 range 522 + float normalizedAngle = (angle + 3.14159f) / (2.0f * 3.14159f); 523 + 524 + // Map to frequency range 525 + g_radio.frequency = 10.0f + normalizedAngle * 24.0f; 526 + 527 + // Clamp frequency 528 + if (g_radio.frequency < 10.0f) g_radio.frequency = 10.0f; 529 + if (g_radio.frequency > 34.0f) g_radio.frequency = 34.0f; 530 + 531 + // Calculate signal strength based on nearest station 532 + RadioStation* nearestStation = FindNearestStation(g_radio.frequency); 533 + if (nearestStation) { 534 + g_radio.signalStrength = (int)(GetStationSignalStrength(nearestStation, g_radio.frequency) * 100.0f); 535 + } else { 536 + g_radio.signalStrength = 5 + (int)(15.0f * sin(g_radio.frequency)); 537 + } 538 + 539 + if (g_radio.signalStrength < 0) g_radio.signalStrength = 0; 540 + if (g_radio.signalStrength > 100) g_radio.signalStrength = 100; 541 + } 542 + 543 + void UpdateVolumeFromMouse(int mouseX, int mouseY) { 544 + float angle = GetAngleFromPoint(mouseX, mouseY, 350, 200); 545 + 546 + // Convert angle to volume (0-1 range) 547 + // Map from -135 degrees to +135 degrees 548 + float normalizedAngle = (angle + 3.14159f * 0.75f) / (3.14159f * 1.5f); 549 + 550 + g_radio.volume = normalizedAngle; 551 + 552 + // Clamp volume 553 + if (g_radio.volume < 0.0f) g_radio.volume = 0.0f; 554 + if (g_radio.volume > 1.0f) g_radio.volume = 1.0f; 555 + } 556 + 557 + int InitializeAudio() { 558 + WAVEFORMATEX waveFormat; 559 + waveFormat.wFormatTag = WAVE_FORMAT_PCM; 560 + waveFormat.nChannels = CHANNELS; 561 + waveFormat.nSamplesPerSec = SAMPLE_RATE; 562 + waveFormat.wBitsPerSample = BITS_PER_SAMPLE; 563 + waveFormat.nBlockAlign = (waveFormat.nChannels * waveFormat.wBitsPerSample) / 8; 564 + waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; 565 + waveFormat.cbSize = 0; 566 + 567 + MMRESULT result = waveOutOpen(&g_audio.hWaveOut, WAVE_MAPPER, &waveFormat, 568 + (DWORD_PTR)WaveOutProc, 0, CALLBACK_FUNCTION); 569 + 570 + if (result != MMSYSERR_NOERROR) { 571 + return -1; 572 + } 573 + 574 + // Initialize audio buffers 575 + for (int i = 0; i < NUM_BUFFERS; i++) { 576 + memset(&g_audio.waveHeaders[i], 0, sizeof(WAVEHDR)); 577 + g_audio.waveHeaders[i].lpData = (LPSTR)g_audio.audioBuffers[i]; 578 + g_audio.waveHeaders[i].dwBufferLength = BUFFER_SIZE * sizeof(short); 579 + g_audio.waveHeaders[i].dwFlags = 0; 580 + 581 + result = waveOutPrepareHeader(g_audio.hWaveOut, &g_audio.waveHeaders[i], sizeof(WAVEHDR)); 582 + if (result != MMSYSERR_NOERROR) { 583 + return -1; 584 + } 585 + } 586 + 587 + g_audio.currentBuffer = 0; 588 + g_audio.isPlaying = 0; 589 + g_audio.staticVolume = 0.3f; 590 + g_audio.radioVolume = 0.0f; 591 + 592 + return 0; 593 + } 594 + 595 + void CleanupAudio() { 596 + if (g_audio.hWaveOut) { 597 + waveOutReset(g_audio.hWaveOut); 598 + 599 + for (int i = 0; i < NUM_BUFFERS; i++) { 600 + waveOutUnprepareHeader(g_audio.hWaveOut, &g_audio.waveHeaders[i], sizeof(WAVEHDR)); 601 + } 602 + 603 + waveOutClose(g_audio.hWaveOut); 604 + g_audio.hWaveOut = NULL; 605 + } 606 + } 607 + 608 + void GenerateStaticBuffer(short* buffer, int size) { 609 + for (int i = 0; i < size; i++) { 610 + // Generate white noise 611 + float noise = ((float)rand() / RAND_MAX) * 2.0f - 1.0f; 612 + 613 + // Apply volume and convert to 16-bit 614 + float volume = g_audio.staticVolume * g_radio.volume; 615 + buffer[i] = (short)(noise * volume * 32767.0f); 616 + } 617 + } 618 + 619 + void FillAudioBuffer(short* buffer, int size) { 620 + // Only generate audio if radio is powered on 621 + if (!g_radio.power) { 622 + memset(buffer, 0, size * sizeof(short)); 623 + return; 624 + } 625 + 626 + // Generate static 627 + GenerateStaticBuffer(buffer, size); 628 + 629 + // Find nearest station and mix in signal 630 + RadioStation* station = FindNearestStation(g_radio.frequency); 631 + if (station && g_radio.signalStrength > 20) { 632 + float signalStrength = GetStationSignalStrength(station, g_radio.frequency); 633 + float radioVolume = signalStrength * g_radio.volume * 0.7f; 634 + 635 + for (int i = 0; i < size; i++) { 636 + // Generate different tones for different stations 637 + float time = (float)i / SAMPLE_RATE; 638 + float tone1 = sin(2.0f * 3.14159f * (station->frequency * 50.0f) * time); 639 + float tone2 = sin(2.0f * 3.14159f * (station->frequency * 75.0f) * time) * 0.5f; 640 + float tone = (tone1 + tone2) * 0.5f; 641 + 642 + // Add some modulation to simulate voice/music 643 + float modulation = sin(2.0f * 3.14159f * 3.0f * time) * 0.3f + 0.7f; 644 + tone *= modulation; 645 + 646 + // Mix tone with existing static 647 + float mixed = (float)buffer[i] / 32767.0f; 648 + mixed = mixed * (1.0f - radioVolume) + tone * radioVolume; 649 + 650 + buffer[i] = (short)(mixed * 32767.0f); 651 + } 652 + } 653 + } 654 + 655 + void CALLBACK WaveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, 656 + DWORD_PTR dwParam1, DWORD_PTR dwParam2) { 657 + if (uMsg == WOM_DONE && g_audio.isPlaying) { 658 + WAVEHDR* waveHeader = (WAVEHDR*)dwParam1; 659 + 660 + // Refill the buffer 661 + FillAudioBuffer((short*)waveHeader->lpData, BUFFER_SIZE); 662 + 663 + // Queue the buffer for playback 664 + waveOutWrite(g_audio.hWaveOut, waveHeader, sizeof(WAVEHDR)); 665 + } 666 + } 667 + 668 + void StartAudio() { 669 + if (!g_audio.isPlaying) { 670 + g_audio.isPlaying = 1; 671 + 672 + // Fill and queue all buffers 673 + for (int i = 0; i < NUM_BUFFERS; i++) { 674 + FillAudioBuffer(g_audio.audioBuffers[i], BUFFER_SIZE); 675 + waveOutWrite(g_audio.hWaveOut, &g_audio.waveHeaders[i], sizeof(WAVEHDR)); 676 + } 677 + } 678 + } 679 + 680 + void StopAudio() { 681 + if (g_audio.isPlaying) { 682 + g_audio.isPlaying = 0; 683 + waveOutReset(g_audio.hWaveOut); 684 + } 685 + } 686 + 687 + RadioStation* FindNearestStation(float frequency) { 688 + RadioStation* nearest = NULL; 689 + float minDistance = 999.0f; 690 + 691 + for (int i = 0; i < NUM_STATIONS; i++) { 692 + float distance = fabs(g_stations[i].frequency - frequency); 693 + if (distance < minDistance) { 694 + minDistance = distance; 695 + nearest = &g_stations[i]; 696 + } 697 + } 698 + 699 + // Only return station if we're close enough (within 0.5 MHz) 700 + if (minDistance <= 0.5f) { 701 + return nearest; 702 + } 703 + 704 + return NULL; 705 + } 706 + 707 + float GetStationSignalStrength(RadioStation* station, float currentFreq) { 708 + if (!station) return 0.0f; 709 + 710 + float distance = fabs(station->frequency - currentFreq); 711 + 712 + // Signal strength drops off with distance from exact frequency 713 + if (distance < 0.05f) { 714 + return 0.9f; // Very strong signal 715 + } else if (distance < 0.1f) { 716 + return 0.7f; // Strong signal 717 + } else if (distance < 0.2f) { 718 + return 0.5f; // Medium signal 719 + } else if (distance < 0.5f) { 720 + return 0.2f; // Weak signal 721 + } 722 + 723 + return 0.0f; // No signal 724 + }
-176
qr.h
··· 1 - #pragma once 2 - #include <windows.h> 3 - 4 - #define QR_SIZE 21 5 - #define MAX_TEXT_LEN 256 6 - 7 - // Pure C struct for QR code - no C++ classes 8 - typedef struct { 9 - BOOL modules[QR_SIZE][QR_SIZE]; 10 - char text[MAX_TEXT_LEN]; 11 - } QRCode; 12 - 13 - // Function prototypes 14 - void QRCode_Init(QRCode* qr, const char* inputText); 15 - void QRCode_GeneratePattern(QRCode* qr); 16 - void QRCode_AddFinderPattern(QRCode* qr, int x, int y); 17 - BOOL QRCode_IsReserved(int x, int y); 18 - void QRCode_DrawToHDC(QRCode* qr, HDC hdc, int startX, int startY, int moduleSize); 19 - int QRCode_GetSize(void); 20 - const char* QRCode_GetText(QRCode* qr); 21 - 22 - // Implementation 23 - void QRCode_Init(QRCode* qr, const char* inputText) { 24 - int x, y, i; 25 - 26 - // Initialize modules array 27 - for (y = 0; y < QR_SIZE; y++) { 28 - for (x = 0; x < QR_SIZE; x++) { 29 - qr->modules[y][x] = FALSE; 30 - } 31 - } 32 - 33 - // Copy text (safe copy) 34 - i = 0; 35 - while (inputText[i] && i < MAX_TEXT_LEN - 1) { 36 - qr->text[i] = inputText[i]; 37 - i++; 38 - } 39 - qr->text[i] = '\0'; 40 - 41 - // Generate pattern 42 - QRCode_GeneratePattern(qr); 43 - } 44 - 45 - void QRCode_GeneratePattern(QRCode* qr) { 46 - int i, x, y; 47 - unsigned int hash = 0; 48 - unsigned char textBytes[MAX_TEXT_LEN]; 49 - int textLen = 0; 50 - 51 - // Add finder patterns (corners) 52 - QRCode_AddFinderPattern(qr, 0, 0); 53 - QRCode_AddFinderPattern(qr, QR_SIZE - 7, 0); 54 - QRCode_AddFinderPattern(qr, 0, QR_SIZE - 7); 55 - 56 - // Add timing patterns 57 - for (i = 8; i < QR_SIZE - 8; i++) { 58 - qr->modules[6][i] = (i % 2 == 0) ? TRUE : FALSE; 59 - qr->modules[i][6] = (i % 2 == 0) ? TRUE : FALSE; 60 - } 61 - 62 - // Convert text to bytes and calculate length 63 - while (qr->text[textLen] && textLen < MAX_TEXT_LEN - 1) { 64 - textBytes[textLen] = (unsigned char)qr->text[textLen]; 65 - textLen++; 66 - } 67 - 68 - // Add format information (fake but realistic looking) 69 - // These would normally encode error correction level and mask pattern 70 - qr->modules[8][0] = TRUE; 71 - qr->modules[8][1] = FALSE; 72 - qr->modules[8][2] = TRUE; 73 - qr->modules[8][3] = TRUE; 74 - qr->modules[8][4] = FALSE; 75 - qr->modules[8][5] = TRUE; 76 - 77 - // Add data in a more realistic zigzag pattern 78 - int bitIndex = 0; 79 - BOOL upward = TRUE; 80 - 81 - for (x = QR_SIZE - 1; x > 0; x -= 2) { 82 - if (x == 6) x--; // Skip timing column 83 - 84 - for (i = 0; i < QR_SIZE; i++) { 85 - y = upward ? (QR_SIZE - 1 - i) : i; 86 - 87 - // Fill two columns (right to left) 88 - for (int col = 0; col < 2; col++) { 89 - int currentX = x - col; 90 - if (currentX >= 0 && !QRCode_IsReserved(currentX, y)) { 91 - // Use text data in a more structured way 92 - BOOL bit = FALSE; 93 - if (bitIndex < textLen * 8) { 94 - int byteIndex = bitIndex / 8; 95 - int bitPos = 7 - (bitIndex % 8); 96 - bit = (textBytes[byteIndex] >> bitPos) & 1; 97 - bitIndex++; 98 - } else { 99 - // Padding pattern 100 - bit = ((currentX + y) % 3 == 0) ? TRUE : FALSE; 101 - } 102 - qr->modules[y][currentX] = bit; 103 - } 104 - } 105 - } 106 - upward = !upward; 107 - } 108 - } 109 - 110 - void QRCode_AddFinderPattern(QRCode* qr, int x, int y) { 111 - int dx, dy; 112 - BOOL dark; 113 - 114 - for (dy = 0; dy < 7; dy++) { 115 - for (dx = 0; dx < 7; dx++) { 116 - if (x + dx < QR_SIZE && y + dy < QR_SIZE) { 117 - dark = (dx == 0 || dx == 6 || dy == 0 || dy == 6 || 118 - (dx >= 2 && dx <= 4 && dy >= 2 && dy <= 4)) ? TRUE : FALSE; 119 - qr->modules[y + dy][x + dx] = dark; 120 - } 121 - } 122 - } 123 - } 124 - 125 - BOOL QRCode_IsReserved(int x, int y) { 126 - // Check if position is part of finder patterns 127 - if ((x < 9 && y < 9) || 128 - (x >= QR_SIZE - 8 && y < 9) || 129 - (x < 9 && y >= QR_SIZE - 8)) { 130 - return TRUE; 131 - } 132 - 133 - // Check timing patterns 134 - if (x == 6 || y == 6) { 135 - return TRUE; 136 - } 137 - 138 - // Check format information areas 139 - if ((x < 9 && y == 8) || (x == 8 && y < 9)) { 140 - return TRUE; 141 - } 142 - if ((x >= QR_SIZE - 8 && y == 8) || (x == 8 && y >= QR_SIZE - 7)) { 143 - return TRUE; 144 - } 145 - 146 - return FALSE; 147 - } 148 - 149 - void QRCode_DrawToHDC(QRCode* qr, HDC hdc, int startX, int startY, int moduleSize) { 150 - HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0)); 151 - HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255)); 152 - int x, y; 153 - RECT rect; 154 - 155 - for (y = 0; y < QR_SIZE; y++) { 156 - for (x = 0; x < QR_SIZE; x++) { 157 - rect.left = startX + x * moduleSize; 158 - rect.top = startY + y * moduleSize; 159 - rect.right = startX + (x + 1) * moduleSize; 160 - rect.bottom = startY + (y + 1) * moduleSize; 161 - 162 - FillRect(hdc, &rect, qr->modules[y][x] ? blackBrush : whiteBrush); 163 - } 164 - } 165 - 166 - DeleteObject(blackBrush); 167 - DeleteObject(whiteBrush); 168 - } 169 - 170 - int QRCode_GetSize(void) { 171 - return QR_SIZE; 172 - } 173 - 174 - const char* QRCode_GetText(QRCode* qr) { 175 - return qr->text; 176 - }