Skip to content

Commit

Permalink
Merge branch '1.19.x/dev' into 1.20.x/dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Kira-NT committed Jun 24, 2024
2 parents 598ae57 + 82dbb9d commit 98259d0
Show file tree
Hide file tree
Showing 33 changed files with 830 additions and 347 deletions.
14 changes: 9 additions & 5 deletions .github/workflows/build-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Setup JDK 17
- name: Get required Java version
id: gradle-properties
uses: christian-draeger/[email protected]
with:
path: gradle.properties
properties: java_version

- name: Setup JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17

- name: Make gradlew executable
run: chmod +x ./gradlew
java-version: ${{ steps.gradle-properties.outputs.java_version }}

- name: Build artifacts
run: ./gradlew clean build
Expand Down
15 changes: 10 additions & 5 deletions .github/workflows/release-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Setup JDK 17
- name: Get required Java version
id: gradle-properties
uses: christian-draeger/[email protected]
with:
path: gradle.properties
properties: java_version

- name: Setup JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17

- name: Make gradlew executable
run: chmod +x ./gradlew
java-version: ${{ steps.gradle-properties.outputs.java_version }}

- name: Build artifacts
run: ./gradlew clean build
Expand All @@ -27,6 +31,7 @@ jobs:
uses: Kir-Antipov/[email protected]
with:
name: ""
game-version-filter: releases | min-major | min-minor
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<img alt="Cubes Without Borders Icon" src="https://raw.githubusercontent.com/Kir-Antipov/cubes-without-borders/HEAD/media/icon.png" width="128">

A mod that allows you to play Minecraft in a borderless fullscreen window. This way, you can have the game open on one monitor, while interacting with other applications on a different monitor, without consistently causing Minecraft to minimize.
A mod that allows you to play Minecraft in a borderless fullscreen window. Whether you're using Linux, macOS, or Windows, you can have the game open on one monitor, while interacting with other applications on a different monitor, without consistently causing Minecraft to minimize.

----

Expand All @@ -27,6 +27,20 @@ Additionally, the mod introduces a `--borderless` startup flag for those interes

----

## Notes

### KDE Plasma

In case you want to open a Picture-in-Picture video on top of your Minecraft gameplay, while you painstakingly mine obsidian pillars in The End for a new mega-project, you may notice that it doesn't quite work as you would expect on KDE Plasma - Minecraft simply renders on top of the supposedly always-on-top window.

Unfortunately, there's nothing I can do on my side, since a borderless fullscreen window is still a fullscreen window, and this behavior is explicitly defined by [the FreeDesktop spec](https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html#STACKINGORDER), which was written long before it became common for people to display PiP windows on top of their fullscreen games. GNOME users don't suffer from this problem, because GNOME simply broke the specification without even attempting to start a discussion around it and change it for everyone's benefit, which is a very GNOME thing to do.

However, you can define a simple window rule to change the PiP's layer to something that renders on top of fullscreen windows, such as `OSD` or `Overlay`. With this in place, you will be able to put PiP windows on top of Minecraft, other games, and any other fullscreen apps for that matter. Here's an example for Firefox users:

<img alt="KDE Plasma - Window Rules" width="886" src="https://raw.githubusercontent.com/Kir-Antipov/cubes-without-borders/HEAD/media/kde-plasma-window-rules.png">

----

## Installation

Requirements:
Expand All @@ -49,27 +63,14 @@ Requirements:

- JDK `17`

### Linux/MacOS

```cmd
git clone https://github.com/Kir-Antipov/cubes-without-borders.git
```bash
git clone https://github.com/Kir-Antipov/cubes-without-borders
cd cubes-without-borders

chmod +x ./gradlew
./gradlew build
cd build/libs
```

### Windows

```cmd
git clone https://github.com/Kir-Antipov/cubes-without-borders.git
cd cubes-without-borders
gradlew build
cd build/libs
```

----

## License
Expand Down
13 changes: 9 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ plugins {
id "org.ajoberstar.grgit" version "4.1.0"
}

sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.toVersion(project.java_version)
targetCompatibility = sourceCompatibility

Integer javaVersion = targetCompatibility.ordinal() + 1
String minecraftDependencyVersion = project.minecraft_version.split(/[+ -]/)[0]
String loaderVersion = project.loader_version;

group = project.maven_group
archivesBaseName = project.archives_base_name
Expand Down Expand Up @@ -37,9 +39,12 @@ dependencies {
}

processResources {
filesMatching("fabric.mod.json") {
filesMatching(["fabric.mod.json", "*.mixins.json"]) {
expand([
"version": version,
"loader": loaderVersion,
"minecraft": minecraftDependencyVersion,
"java": javaVersion,
])
}
}
Expand All @@ -50,7 +55,7 @@ tasks.withType(JavaCompile).configureEach {
// If Javadoc is generated, this must be specified in that task too.
// See http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
it.options.encoding = "UTF-8"
it.options.release.set(17)
it.options.release.set(javaVersion)
}

java {
Expand Down
5 changes: 3 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ org.gradle.jvmargs=-Xmx1G
# Fabric Properties
minecraft_version=1.20
yarn_mappings=1.20+build.1
loader_version=0.15.6
loader_version=0.15.0
java_version=17

# Mod Properties
mod_version=1.1.0
mod_version=2.0.0
maven_group=dev.kir
archives_base_name=cubes-without-borders

Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.
Binary file added media/kde-plasma-window-rules.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.kir.cubeswithoutborders.compat.sodium;
package dev.kir.cubeswithoutborders.client.compat.sodium;

import net.fabricmc.loader.api.FabricLoader;
import org.objectweb.asm.tree.ClassNode;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.kir.cubeswithoutborders.compat.sodium.mixin;
package dev.kir.cubeswithoutborders.client.compat.sodium.mixin;

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package dev.kir.cubeswithoutborders.client.mixin;

import com.mojang.serialization.Codec;
import dev.kir.cubeswithoutborders.client.util.FullscreenWindowState;
import dev.kir.cubeswithoutborders.client.option.FullscreenOptions;
import dev.kir.cubeswithoutborders.client.option.FullscreenMode;
import dev.kir.cubeswithoutborders.client.option.SimpleOptionCallbacks;
import dev.kir.cubeswithoutborders.client.util.MonitorInfo;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.GameOptions;
import net.minecraft.client.option.SimpleOption;
import net.minecraft.client.util.Window;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.io.File;
import java.util.Arrays;

@Environment(EnvType.CLIENT)
@Mixin(GameOptions.class)
abstract class GameOptionsMixin implements FullscreenOptions {
private SimpleOption<MonitorInfo> fullscreenMonitor;

private SimpleOption<FullscreenMode> fullscreenMode;

private SimpleOption<FullscreenMode> preferredFullscreenMode;

@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/option/GameOptions;load()V", shift = At.Shift.BEFORE))
private void init(MinecraftClient client, File optionsFile, CallbackInfo ci) {
this.fullscreenMonitor = new SimpleOption<>(
"options.fullscreenMonitor",
SimpleOption.emptyTooltip(),
(option, value) -> Text.literal(value.toString()),
new SimpleOptionCallbacks<>(
Codec.STRING.xmap(x -> MonitorInfo.parse(x).orElse(MonitorInfo.primary()), MonitorInfo::toString)
),
MonitorInfo.primary(),
value -> { }
);

this.fullscreenMode = new SimpleOption<>(
"options.fullscreen",
SimpleOption.emptyTooltip(),
SimpleOption.enumValueText(),
new SimpleOption.PotentialValuesBasedCallbacks<>(Arrays.asList(FullscreenMode.values()), Codec.INT.xmap(FullscreenMode::get, FullscreenMode::getId)),
FullscreenMode.OFF,
value -> {
Window window = MinecraftClient.getInstance().getWindow();
FullscreenWindowState borderlessWindow = (FullscreenWindowState)(Object)window;
if (window == null || value == borderlessWindow.getFullscreenMode()) {
return;
}

borderlessWindow.setFullscreenMode(value);
FullscreenMode fullscreenMode = borderlessWindow.getFullscreenMode();
FullscreenMode preferredFullscreenMode = borderlessWindow.getPreferredFullscreenMode();

this.getFullscreenMode().setValue(fullscreenMode);
this.getPreferredFullscreenMode().setValue(preferredFullscreenMode);
this.getFullscreen().setValue(fullscreenMode == FullscreenMode.ON);
}
);

this.preferredFullscreenMode = new SimpleOption<>(
"options.preferredFullscreenMode",
SimpleOption.emptyTooltip(),
SimpleOption.enumValueText(),
new SimpleOption.PotentialValuesBasedCallbacks<>(Arrays.asList(FullscreenMode.values()), Codec.INT.xmap(FullscreenMode::get, FullscreenMode::getId)),
FullscreenMode.ON,
value -> {
Window window = MinecraftClient.getInstance().getWindow();
FullscreenWindowState borderlessWindow = (FullscreenWindowState)(Object)window;
if (window == null || value == borderlessWindow.getPreferredFullscreenMode()) {
return;
}

borderlessWindow.setPreferredFullscreenMode(value);
FullscreenMode preferredFullscreenMode = borderlessWindow.getPreferredFullscreenMode();

this.getPreferredFullscreenMode().setValue(preferredFullscreenMode);
}
);
}

@Inject(method = "accept", at = @At("HEAD"))
private void accept(GameOptions.Visitor visitor, CallbackInfo ci) {
visitor.accept("fullscreenMonitor", this.fullscreenMonitor);
visitor.accept("fullscreenMode", this.fullscreenMode);
visitor.accept("preferredFullscreenMode", this.preferredFullscreenMode);
}

@Shadow
public abstract SimpleOption<Boolean> getFullscreen();

@Override
public SimpleOption<MonitorInfo> getFullscreenMonitor() {
return this.fullscreenMonitor;
}

@Override
public SimpleOption<FullscreenMode> getFullscreenMode() {
return this.fullscreenMode;
}

@Override
public SimpleOption<FullscreenMode> getPreferredFullscreenMode() {
return this.preferredFullscreenMode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package dev.kir.cubeswithoutborders.client.mixin;

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import dev.kir.cubeswithoutborders.client.util.FullscreenWindowState;
import dev.kir.cubeswithoutborders.client.option.FullscreenMode;
import dev.kir.cubeswithoutborders.client.option.FullscreenOptions;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Keyboard;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.SimpleOption;
import net.minecraft.client.util.Window;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;

@Environment(EnvType.CLIENT)
@Mixin(value = Keyboard.class, priority = 1000000)
abstract class KeyboardMixin {
@Shadow
private @Final MinecraftClient client;

@WrapOperation(method = "onKey", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/Window;toggleFullscreen()V", ordinal = 0))
private void toggleFullscreen(@NotNull Window window, Operation<Void> _toggleFullscreen) {
FullscreenWindowState borderlessWindow = (FullscreenWindowState)(Object)window;
FullscreenOptions options = (FullscreenOptions)this.client.options;
SimpleOption<FullscreenMode> fullscreenMode = options.getFullscreenMode();
SimpleOption<FullscreenMode> preferredFullscreenMode = options.getPreferredFullscreenMode();

fullscreenMode.setValue(borderlessWindow.toggleFullscreenMode());
preferredFullscreenMode.setValue(borderlessWindow.getPreferredFullscreenMode());
}

@WrapOperation(method = "onKey", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/option/SimpleOption;setValue(Ljava/lang/Object;)V"))
private <T> void saveOptions(SimpleOption<T> option, T value, Operation<Void> setValue) {
// Minecraft doesn't save options when they are changed during a key press.
// This means that, for example, if you toggle fullscreen mode via F11,
// the new window state won't be preserved upon a game restart.
// So, we force Minecraft to save options whenever some new value is set.
setValue.call(option, value);
this.client.options.write();
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package dev.kir.cubeswithoutborders.mixin;
package dev.kir.cubeswithoutborders.client.mixin;

import com.llamalad7.mixinextras.injector.ModifyReceiver;
import com.llamalad7.mixinextras.sugar.Local;
import dev.kir.cubeswithoutborders.client.BorderlessWindowState;
import dev.kir.cubeswithoutborders.client.util.FullscreenWindowState;
import dev.kir.cubeswithoutborders.client.option.FullscreenMode;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import net.fabricmc.api.EnvType;
Expand All @@ -26,8 +27,11 @@ private static OptionParser allowBorderlessOption(OptionParser parser, String[]
@Inject(method = "main", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;beginInitialization()V", ordinal = 0), require = 0)
private static void readBorderlessOption(String[] args, CallbackInfo ci, @Local(ordinal = 0) OptionSet options, @Local(ordinal = 0) RunArgs runArgs) {
boolean isBorderless = options.has("borderless");
BorderlessWindowState windowSettings = (BorderlessWindowState)runArgs.windowSettings;
if (!isBorderless) {
return;
}

windowSettings.setBorderless(isBorderless);
FullscreenWindowState windowSettings = (FullscreenWindowState)runArgs.windowSettings;
windowSettings.setFullscreenMode(FullscreenMode.BORDERLESS);
}
}
Loading

0 comments on commit 98259d0

Please sign in to comment.