Skip to content

Conversation

@NoahJCross
Copy link

@NoahJCross NoahJCross commented Jan 6, 2025

Description

Added Rust as a supported language within SKM's Foreign Function Interface (FFI) system. This update enables SKM to handle Rust FFI calls alongside the existing supported languages, allowing Rust programs to properly interface with SplashKit's core functionality.

Changes Made

  • Added Rust language handling to SKM's FFI system
  • Implemented necessary FFI adapters for Rust integration
  • Added Rust to SKM's supported languages list
  • Integrated Rust FFI calling conventions into existing SKM infrastructure

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

  • Completed testing of Rust FFI functionality

Testing Note

I’m building a test runner to make testing more accessible for other users. This will be provided in a future PR with the tests. In the meantime I’ll provide a tutorial on how you can run a rust program.

Why this manual setup is necessary:

The SplashKit installation scripts download from the GitHub source project regardless of running the installation script locally. This means the files must be manually copied to your SplashKit directory to use the Rust implementation. The following steps ensure proper setup of the Rust environment for SplashKit.

Setting Up a Rust Project with SplashKit SDK

Prerequisites

  • SplashKit SDK must be installed
  • Git installed

Step 1: Clone the Repository

git clone https://github.com/NoahJCross/skm
cd skm

Step 2: Install dos2unix

Choose the appropriate command for your system:

  • MSYS2/MINGW64:
    pacman -S --needed --disable-download-timeout dos2unix
  • macOS:
    brew install dos2unix
  • Linux (Ubuntu):
    sudo apt-get install dos2unix

Step 3: Create Directory Structure

mkdir -p ~/.splashkit/{rust/src,new/rust/files/src,fix/rust}

Step 4: Copy Files

Copy entire directories with -r:

cp -r rust/{build.rs,Cargo.toml} ~/.splashkit/rust/
cp -r rust/src/lib.rs ~/.splashkit/rust/src/
cp -r new/rust/files/Cargo.toml ~/.splashkit/new/rust/files/
cp -r new/rust/files/src/main.rs ~/.splashkit/new/rust/files/src/
cp -r new/rust/{skm_new_rust.sh,lang_details.sh} ~/.splashkit/new/rust/
cp -r fix/rust/skm_fix_rust.sh ~/.splashkit/fix/rust/
cp -r new/{help.sh,new.sh} ~/.splashkit/new/
cp skm ~/.splashkit/

Step 5: Convert Line Endings

dos2unix ~/.splashkit/new/rust/skm_new_rust.sh
dos2unix ~/.splashkit/new/rust/lang_details.sh
dos2unix ~/.splashkit/fix/rust/skm_fix_rust.sh
dos2unix ~/.splashkit/new/help.sh
dos2unix ~/.splashkit/new/new.sh
dos2unix ~/.splashkit/skm

Step 6: Set File Permissions

chmod 755 ~/.splashkit/new/rust/skm_new_rust.sh
chmod 755 ~/.splashkit/new/rust/lang_details.sh
chmod 755 ~/.splashkit/fix/rust/skm_fix_rust.sh
chmod 755 ~/.splashkit/new/help.sh
chmod 755 ~/.splashkit/new/new.sh
chmod 755 ~/.splashkit/skm

Step 7: Create and Run a Test Project

mkdir rust_test
cd rust_test
skm new cargo

At this point, you can either create your own program or use the provided sample code. Copy the sample code into src/main.rs.

Step 8: Build and Run

skm cargo build && skm cargo run

This will compile and run your Rust SplashKit project. The sample code creates a simple coin collector game with player movement, coins to collect, and enemies to avoid.

use splashkit::*;

fn main() {
    open_window("Coin Collector".to_string(), 800, 600);
    
    let bmp = create_bitmap("player".to_string(), 32, 32);
    clear_bitmap(bmp, color_blue());
    setup_collision_mask(bmp);
    
    let mut player_x: f64 = 400.0;
    let mut player_y: f64 = 300.0;
    let player_speed: f64 = 4.0;
    let player_size: f64 = 30.0;
    
    let mut score = 0;
    let mut game_time: f64 = 0.0;
    let mut is_game_over = false;
    let mut immunity_time: f64 = 3.0;
    
    let mut coins = Vec::new();
    let mut enemies = Vec::new();
    
    for _ in 0..5 {
        coins.push((
            rnd_int(700) as f64,
            rnd_int(500) as f64
        ));
        
        enemies.push((
          Rectangle {
              x: rnd_int(700) as f64,
              y: rnd_int(500) as f64,
              width: player_size,
              height: player_size
          },
          (rnd_int(2) as f64 + 1.0) * 0.5  
      ));
    }
    
    while !window_close_requested_named("Coin Collector".to_string()) {
        process_events();
        
        if !is_game_over {
            if key_down(KeyCode::LeftKey) {
                player_x -= player_speed;
            }
            if key_down(KeyCode::RightKey) {
                player_x += player_speed;
            }
            if key_down(KeyCode::UpKey) {
                player_y -= player_speed;
            }
            if key_down(KeyCode::DownKey) {
                player_y += player_speed;
            }
            
            player_x = player_x.clamp(0.0, 800.0 - player_size);
            player_y = player_y.clamp(0.0, 600.0 - player_size);

            for (enemy_rect, speed) in enemies.iter_mut() {
              if enemy_rect.x < player_x { enemy_rect.x += *speed; }
              if enemy_rect.x > player_x { enemy_rect.x -= *speed; }
              if enemy_rect.y < player_y { enemy_rect.y += *speed; }
              if enemy_rect.y > player_y { enemy_rect.y -= *speed; }
              
              if immunity_time <= 0.0 {
                  if bitmap_rectangle_collision(bmp, player_x, player_y, *enemy_rect) {
                      is_game_over = true;
                  }
              }
            }
            
            let mut new_coins = Vec::new();
            coins.retain(|(coin_x, coin_y)| {
                let coin_circle = Circle {
                    center: Point2D { x: *coin_x, y: *coin_y },
                    radius: 20.0
                };
                
                let collected = bitmap_circle_collision(bmp, player_x, player_y, coin_circle);
                if collected {
                    score += 10;
                    new_coins.push((rnd_int(700) as f64, rnd_int(500) as f64));
                }
                !collected
            });
            coins.extend(new_coins);
            
            game_time += 1.0 / 60.0;
            immunity_time -= 1.0 / 60.0;
        }
        
        clear_screen(color_black());
        
        for (coin_x, coin_y) in coins.iter() {
            fill_circle(color_yellow(), *coin_x, *coin_y, 10.0);
        }
        
        for (enemy_rect, _) in enemies.iter() {
          fill_rectangle_record(color_red(), *enemy_rect);
        }
        
        fill_rectangle(color_blue(), player_x, player_y, player_size, player_size);
        
        draw_text_no_font_no_size(format!("Score: {}", score).to_string(), color_white(), 10.0, 10.0);
        draw_text_no_font_no_size(format!("Time: {:.1}s", game_time).to_string(), color_white(), 10.0, 30.0);
        
        if is_game_over {
            draw_text_no_font_no_size("Game Over!".to_string(), color_red(), 350.0, 300.0);
            draw_text_no_font_no_size("Close window to exit".to_string(), color_white(), 320.0, 330.0);
        }
        
        refresh_screen_with_target_fps(60);
    }
}

Checklist

  • Code follows the style guidelines of this project
  • Self-review completed
  • Changes generate no new warnings
  • Testing completed and verified working
  • Provided example program

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant