message: Fixed comparison process.

diff:
@@ -201,8 +201,15 @@ internal class KotlinInstantiators(
defaultInstantiator: ValueInstantiator
): ValueInstantiator {
return if (beanDescriptor.beanClass.isKotlinClass()) {
- if (defaultInstantiator is StdValueInstantiator) {
- KotlinValueInstantiator(defaultInstantiator, cache, nullToEmptyCollection, nullToEmptyMap, nullIsSameAsDefault, strictNullChecks)
+ if (defaultInstantiator::class == StdValueInstantiator::class) {
+ KotlinValueInstantiator(
+ defaultInstantiator as StdValueInstantiator,
+ cache,
+ nullToEmptyCollection,
+ nullToEmptyMap,
+ nullIsSameAsDefault,
+ strictNullChecks
+ )
} else {
// TODO: return defaultInstantiator and let default method parameters and nullability go unused? or die with exception:
throw IllegalStateException("KotlinValueInstantiator requires that the default ValueInstantiator is StdValueInstantiator")

original_message: Fixed comparison process.


message: add ValueClassBoxSerializer

change_type: MODIFY

diff:
@@ -9,6 +9,7 @@ import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.ser.Serializers
import com.fasterxml.jackson.databind.ser.std.StdSerializer
import java.math.BigInteger
+import kotlin.reflect.KClass
object SequenceSerializer : StdSerializer<Sequence<*>>(Sequence::class.java) {
override fun serialize(value: Sequence<*>, gen: JsonGenerator, provider: SerializerProvider) {
@@ -71,3 +72,20 @@ internal class KotlinSerializers : Serializers.Base() {
else -> null
}
}
+
+// This serializer is used to properly serialize the value class.
+// The getter generated for the value class is special,
+// so this class will not work properly when added to the Serializers
+// (it is configured from KotlinAnnotationIntrospector.findSerializer).
+internal class ValueClassBoxSerializer(
+ outerClazz: KClass<Any>, innerClazz: Class<*>
+) : StdSerializer<Any>(outerClazz.java) {
+ private val boxMethod = _handledType.getMethod("box-impl", innerClazz)
+
+ override fun serialize(value: Any, gen: JsonGenerator, provider: SerializerProvider) {
+ // Values retrieved from getter are considered validated and constructor-impl is not executed.
+ val boxed = boxMethod.invoke(null, value)
+
+ provider.findValueSerializer(_handledType).serialize(boxed, gen, provider)
+ }
+}

license: Apache License 2.0

repo: fasterxml/jackson-module-kotlin

original_message: add ValueClassBoxSerializer


message: add findeSerializer

diff:
@@ -11,12 +11,14 @@ import java.lang.reflect.AccessibleObject
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Method
+import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty1
import kotlin.reflect.KType
import kotlin.reflect.full.createType
import kotlin.reflect.full.declaredMemberProperties
+import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.*
@@ -59,6 +61,39 @@ internal class KotlinAnnotationIntrospector(private val context: Module.SetupCon
return super.findCreatorAnnotation(config, a)
}
+ // Find a serializer to handle the case where the getter returns an unboxed value from the value class.
+ override fun findSerializer(am: Annotated): ValueClassBoxSerializer? = when (am) {
+ is AnnotatedMethod -> {
+ val getter = am.member.apply {
+ // If the return value of the getter is a value class,
+ // it will be serialized properly without doing anything.
+ if (this.returnType.isUnboxableValueClass()) return null
+ }
+
+ val kotlinProperty = getter
+ .declaringClass
+ .kotlin
+ .let {
+ // KotlinReflectionInternalError is raised in GitHub167 test,
+ // but it looks like an edge case, so it is ignored.
+ try {
+ it.memberProperties
+ } catch (e: Error) {
+ null
+ }
+ }?.find { it.javaGetter == getter }
+
+ (kotlinProperty?.returnType?.classifier as? KClass<*>)
+ ?.takeIf { it.isValue }
+ ?.let { outerClazz ->
+ @Suppress("UNCHECKED_CAST")
+ ValueClassBoxSerializer(outerClazz as KClass<Any>, getter.returnType)
+ }
+ }
+ // Ignore the case of AnnotatedField, because JvmField cannot be set in the field of value class.
+ else -> null
+ }
+
/**
* Subclasses can be detected automatically for sealed classes, since all possible subclasses are known
* at compile-time to Kotlin. This makes [com.fasterxml.jackson.annotation.JsonSubTypes] redundant.

original_message: add findeSerializer


message: added consideration for cases where the value wrapped by value class is null

diff:
@@ -94,6 +94,9 @@ internal class KotlinAnnotationIntrospector(private val context: Module.SetupCon
else -> null
}
+ // Perform proper serialization even if the value wrapped by the value class is null.
+ override fun findNullSerializer(am: Annotated) = findSerializer(am)
+
/**
* Subclasses can be detected automatically for sealed classes, since all possible subclasses are known
* at compile-time to Kotlin. This makes [com.fasterxml.jackson.annotation.JsonSubTypes] redundant.

diff:
@@ -82,7 +82,7 @@ internal class ValueClassBoxSerializer(
) : StdSerializer<Any>(outerClazz.java) {
private val boxMethod = _handledType.getMethod("box-impl", innerClazz)
- override fun serialize(value: Any, gen: JsonGenerator, provider: SerializerProvider) {
+ override fun serialize(value: Any?, gen: JsonGenerator, provider: SerializerProvider) {
// Values retrieved from getter are considered validated and constructor-impl is not executed.
val boxed = boxMethod.invoke(null, value)


message: fix for unboxed cases

diff:
@@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.Module
import com.fasterxml.jackson.databind.cfg.MapperConfig
import com.fasterxml.jackson.databind.introspect.*
import com.fasterxml.jackson.databind.jsontype.NamedType
+import com.fasterxml.jackson.databind.ser.std.StdSerializer
import java.lang.reflect.AccessibleObject
import java.lang.reflect.Constructor
import java.lang.reflect.Field
@@ -62,7 +63,7 @@ internal class KotlinAnnotationIntrospector(private val context: Module.SetupCon
}
// Find a serializer to handle the case where the getter returns an unboxed value from the value class.
- override fun findSerializer(am: Annotated): ValueClassBoxSerializer<*>? = when (am) {
+ override fun findSerializer(am: Annotated): StdSerializer<*>? = when (am) {
is AnnotatedMethod -> {
val getter = am.member.apply {
// If the return value of the getter is a value class,
@@ -87,8 +88,10 @@ internal class KotlinAnnotationIntrospector(private val context: Module.SetupCon
?.takeIf { it.isValue }
?.java
?.let { outerClazz ->
- @Suppress("UNCHECKED_CAST")
- ValueClassBoxSerializer(outerClazz, getter.returnType)
+ val innerClazz = getter.returnType
+
+ ValueClassStaticJsonValueSerializer.createdOrNull(outerClazz, innerClazz)
+ ?: @Suppress("UNCHECKED_CAST") ValueClassBoxSerializer(outerClazz, innerClazz)
}
}
// Ignore the case of AnnotatedField, because JvmField cannot be set in the field of value class.

diff:
package com.fasterxml.jackson.module.kotlin
+import com.fasterxml.jackson.annotation.JsonValue
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.BeanDescription
import com.fasterxml.jackson.databind.JavaType
@@ -8,8 +9,9 @@ import com.fasterxml.jackson.databind.SerializationConfig
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.ser.Serializers
import com.fasterxml.jackson.databind.ser.std.StdSerializer
+import java.lang.reflect.Method
+import java.lang.reflect.Modifier
import java.math.BigInteger
-import kotlin.reflect.KClass
object SequenceSerializer : StdSerializer<Sequence<*>>(Sequence::class.java) {
override fun serialize(value: Sequence<*>, gen: JsonGenerator, provider: SerializerProvider) {
@@ -43,6 +45,10 @@ object ULongSerializer : StdSerializer<ULong>(ULong::class.java) {
}
}
+// Class must be UnboxableValueClass.
+private fun Class<*>.getStaticJsonValueGetter(): Method? = this.declaredMethods
+ .find { method -> Modifier.isStatic(method.modifiers) && method.annotations.any { it is JsonValue } }
+
object ValueClassUnboxSerializer : StdSerializer<Any>(Any::class.java) {
override fun serialize(value: Any, gen: JsonGenerator, provider: SerializerProvider) {
val unboxed = value::class.java.getMethod("unbox-impl").invoke(value)
@@ -89,3 +95,27 @@ internal class ValueClassBoxSerializer<T : Any>(
provider.findValueSerializer(outerClazz).serialize(boxed, gen, provider)
}
}
+
+internal class ValueClassStaticJsonValueSerializer<T> private constructor(
+ innerClazz: Class<T>,
+ private val staticJsonValueGetter: Method
+) : StdSerializer<T>(innerClazz) {
+ override fun serialize(value: T?, gen: JsonGenerator, provider: SerializerProvider) {
+ // As shown in the processing of the factory function, jsonValueGetter is always a static method.
+ val jsonValue: Any? = staticJsonValueGetter.invoke(null, value)
+ jsonValue
+ ?.let { provider.findValueSerializer(it::class.java).serialize(it, gen, provider) }
+ ?: provider.findNullValueSerializer(null).serialize(null, gen, provider)
+ }
+
+ // Since JsonValue can be processed correctly if it is given to a non-static getter/field,
+ // this class will only process if it is a `static` method.
+ companion object {
+ fun <T> createdOrNull(
+ outerClazz: Class<out Any>,
+ innerClazz: Class<T>
+ ): ValueClassStaticJsonValueSerializer<T>? = outerClazz
+ .getStaticJsonValueGetter()
+ ?.let { ValueClassStaticJsonValueSerializer(innerClazz, it) }
+ }
+}

original_message: fix for unboxed cases


message: fix for boxed cases

diff:
@@ -49,7 +49,8 @@ object ULongSerializer : StdSerializer<ULong>(ULong::class.java) {
private fun Class<*>.getStaticJsonValueGetter(): Method? = this.declaredMethods
.find { method -> Modifier.isStatic(method.modifiers) && method.annotations.any { it is JsonValue } }
-object ValueClassUnboxSerializer : StdSerializer<Any>(Any::class.java) {
+internal sealed class ValueClassSerializer<T : Any>(t: Class<T>) : StdSerializer<T>(t) {
+ object Unbox : ValueClassSerializer<Any>(Any::class.java) {
override fun serialize(value: Any, gen: JsonGenerator, provider: SerializerProvider) {
val unboxed = value::class.java.getMethod("unbox-impl").invoke(value)
@@ -62,6 +63,32 @@ object ValueClassUnboxSerializer : StdSerializer<Any>(Any::class.java) {
}
}
+ class StaticJsonValue<T : Any>(
+ t: Class<T>, private val staticJsonValueGetter: Method
+ ) : ValueClassSerializer<T>(t) {
+ private val unboxMethod: Method = t.getMethod("unbox-impl")
+
+ override fun serialize(value: T, gen: JsonGenerator, provider: SerializerProvider) {
+ val unboxed = unboxMethod.invoke(value)
+ // As shown in the processing of the factory function, jsonValueGetter is always a static method.
+ val jsonValue: Any? = staticJsonValueGetter.invoke(null, unboxed)
+ jsonValue
+ ?.let { provider.findValueSerializer(it::class.java).serialize(it, gen, provider) }
+ ?: provider.findNullValueSerializer(null).serialize(null, gen, provider)
+ }
+ }
+
+ companion object {
+ // `t` must be UnboxableValueClass.
+ // If create a function with a JsonValue in the value class,
+ // it will be compiled as a static method (= cannot be processed properly by Jackson),
+ // so use a ValueClassSerializer.StaticJsonValue to handle this.
+ fun from(t: Class<*>): ValueClassSerializer<*> = t.getStaticJsonValueGetter()
+ ?.let { StaticJsonValue(t, it) }
+ ?: Unbox
+ }
+}
+
internal class KotlinSerializers : Serializers.Base() {
override fun findSerializer(
config: SerializationConfig?,
@@ -74,7 +101,7 @@ internal class KotlinSerializers : Serializers.Base() {
UInt::class.java.isAssignableFrom(type.rawClass) -> UIntSerializer
ULong::class.java.isAssignableFrom(type.rawClass) -> ULongSerializer
// The priority of Unboxing needs to be lowered so as not to break the serialization of Unsigned Integers.
- type.rawClass.isUnboxableValueClass() -> ValueClassUnboxSerializer
+ type.rawClass.isUnboxableValueClass() -> ValueClassSerializer.from(type.rawClass)
else -> null
}
}

original_message: fix for boxed cases


message: In order to reduce processing costs,  not rewrite if not necessary.

diff:
@@ -7,6 +7,6 @@ internal class ConstructorValueCreator<T>(override val callable: KFunction<T>) :
override val accessible: Boolean = callable.isAccessible
init {
- callable.isAccessible = true
+ if (!accessible) callable.isAccessible = true
}
}


message: Fix the call would fail if callable.isAccessible was false.

diff:
@@ -23,11 +23,15 @@ internal class MethodValueCreator<T> private constructor(
// abort, we have some unknown case here
if (!possibleCompanion.isCompanion) return null
+ val initialCallableAccessible = callable.isAccessible
+ if (!initialCallableAccessible) callable.isAccessible = true
+
val (companionObjectInstance: Any, accessible: Boolean) = try {
// throws ex
val instance = possibleCompanion.objectInstance!!
+
// If an instance of the companion object can be obtained, accessibility depends on the KFunction
- instance to callable.isAccessible
+ instance to initialCallableAccessible
} catch (ex: IllegalAccessException) {
// fallback for when an odd access exception happens through Kotlin reflection
possibleCompanion.java.enclosingClass.fields


message: Fix the call would fail if the companion object was private.

diff:
@@ -34,7 +34,7 @@ internal class MethodValueCreator<T> private constructor(
instance to initialCallableAccessible
} catch (ex: IllegalAccessException) {
// fallback for when an odd access exception happens through Kotlin reflection
- possibleCompanion.java.enclosingClass.fields
+ possibleCompanion.java.enclosingClass.declaredFields
.firstOrNull { it.type.kotlin.isCompanion }
?.let {
it.isAccessible = true


message:
Resolving incompatibilities in 2.13.2
Changes to undo binary incompatibility changes.

diff:
<plugin>
<groupId>com.github.siom79.japicmp</groupId>
<artifactId>japicmp-maven-plugin</artifactId>
- <version>0.15.3</version>
+ <version>0.15.7</version>
<configuration>
<oldVersion>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
- <version>2.13.0</version>
+ <version>2.13.1</version>
<type>jar</type>
</dependency>
</oldVersion>

diff:
@@ -49,13 +49,12 @@ object ULongSerializer : StdSerializer<ULong>(ULong::class.java) {
private fun Class<*>.getStaticJsonValueGetter(): Method? = this.declaredMethods
.find { method -> Modifier.isStatic(method.modifiers) && method.annotations.any { it is JsonValue } }
-internal sealed class ValueClassSerializer<T : Any>(t: Class<T>) : StdSerializer<T>(t) {
- object Unbox : ValueClassSerializer<Any>(Any::class.java) {
+object ValueClassUnboxSerializer : StdSerializer<Any>(Any::class.java) {
override fun serialize(value: Any, gen: JsonGenerator, provider: SerializerProvider) {
val unboxed = value::class.java.getMethod("unbox-impl").invoke(value)
if (unboxed == null) {
- provider.findNullValueSerializer(null).serialize(unboxed, gen, provider)
+ provider.findNullValueSerializer(null).serialize(null, gen, provider)
return
}
@@ -63,6 +62,7 @@ internal sealed class ValueClassSerializer<T : Any>(t: Class<T>) : StdSerializer
}
}
+internal sealed class ValueClassSerializer<T : Any>(t: Class<T>) : StdSerializer<T>(t) {
class StaticJsonValue<T : Any>(
t: Class<T>, private val staticJsonValueGetter: Method
) : ValueClassSerializer<T>(t) {
@@ -83,9 +83,9 @@ internal sealed class ValueClassSerializer<T : Any>(t: Class<T>) : StdSerializer
// If create a function with a JsonValue in the value class,
// it will be compiled as a static method (= cannot be processed properly by Jackson),
// so use a ValueClassSerializer.StaticJsonValue to handle this.
- fun from(t: Class<*>): ValueClassSerializer<*> = t.getStaticJsonValueGetter()
+ fun from(t: Class<*>): StdSerializer<*> = t.getStaticJsonValueGetter()
?.let { StaticJsonValue(t, it) }
- ?: Unbox
+ ?: ValueClassUnboxSerializer
}
}

original_message:
Resolving incompatibilities in 2.13.2 (#551)

Changes to undo binary incompatibility changes.


message:
fix(bls): sign cid instead of entire message
style: import `UnsignedMessager`, refactored qualified path in `key_recover_bls`

diff:
@@ -10,7 +10,7 @@ use forest_address::{Address, BLSPublicKey, Network, Protocol};
use forest_cid::{multihash::MultihashDigest, Cid, Code::Blake2b256, Code::Identity, Codec};
use forest_encoding::blake2b_256;
use forest_encoding::{from_slice, to_vec};
-use forest_message::SignedMessage;
+use forest_message::{SignedMessage, UnsignedMessage};
use num_bigint_chainsafe::BigInt;
use num_traits::FromPrimitive;
use rayon::prelude::*;
@@ -276,7 +276,7 @@ pub fn key_recover_bls(
pub fn transaction_serialize(
unsigned_message_arg: &UnsignedMessageAPI,
) -> Result<CborBuffer, SignerError> {
- let unsigned_message = forest_message::UnsignedMessage::try_from(unsigned_message_arg)?;
+ let unsigned_message = UnsignedMessage::try_from(unsigned_message_arg)?;
let message_cbor = CborBuffer(to_vec(&unsigned_message)?);
Ok(message_cbor)
}
@@ -329,10 +329,12 @@ fn transaction_sign_bls_raw(
unsigned_message_api: &UnsignedMessageAPI,
private_key: &PrivateKey,
) -> Result<SignatureBLS, SignerError> {
- let message_cbor = transaction_serialize(unsigned_message_api)?;
-
let sk = bls_signatures::PrivateKey::from_bytes(&private_key.0)?;
- let sig = sk.sign(&message_cbor.0);
+
+ let unsigned_message = UnsignedMessage::try_from(unsigned_message_api)?;
+
+ //sign the message's signing bytes
+ let sig = sk.sign(unsigned_message.to_signing_bytes());
Ok(SignatureBLS::try_from(sig.as_bytes())?)
}

repo: zondax/filecoin-signing-tools


message: feat: add `getSigningBytes`

diff:
@@ -996,3 +996,17 @@ pub fn get_cid(signed_message: JsValue) -> Result<String, JsValue> {
Ok(result)
}
+
+#[wasm_bindgen(js_name = getSigningBytes)]
+pub fn get_signing_bytes(unsigned_message: JsValue) -> Result<Vec<u8>, JsValue> {
+ set_panic_hook();
+
+ let unsigned_message_api: UnsignedMessageAPI = unsigned_message
+ .into_serde()
+ .map_err(|e| JsValue::from(format!("Error parsing parameters: {}", e)))?;
+
+ let result = filecoin_signer::get_signing_bytes(unsigned_message_api)
+ .map_err(|e| JsValue::from(format!("Error getting the cid: {}", e)))?;
+
+ Ok(result)
+}

diff:
@@ -1217,3 +1217,16 @@ pub fn get_cid(signed_message_api: SignedMessageAPI) -> Result<String, SignerErr
Ok(cid.to_string())
}
+
+/// Return the bytes to sign of an unsigned message
+///
+/// # Arguments
+///
+/// * `unsigned_message_api` - The unsigned message;
+pub fn get_signing_bytes(unsigned_message_api: UnsignedMessageAPI) -> Result<Vec<u8>, SignerError> {
+ let unsigned_message = UnsignedMessage::try_from(&unsigned_message_api)?;
+
+ let bytes = unsigned_message.to_signing_bytes();
+
+ Ok(bytes)
+}

original_message: feat: add `getSigningBytes`


message: fix(bls): signature verification

diff:
@@ -437,14 +437,16 @@ fn verify_bls_signature(
) -> Result<bool, SignerError> {
// TODO: need a function to extract from public key from cbor buffer directly
let message = transaction_parse(cbor_buffer, true)?;
+ let message = message.get_message();
- let address = Address::from_str(&message.get_message().from)?;
+ let address = Address::from_str(&message.from)?;
let pk = bls_signatures::PublicKey::from_bytes(&address.payload_bytes())?;
let sig = bls_signatures::Signature::from_bytes(signature.as_ref())?;
- let result = pk.verify(sig, cbor_buffer.as_ref());
+ let signing_bytes = get_signing_bytes(message)?;
+ let result = pk.verify(sig, signing_bytes);
Ok(result)
}
@@ -483,6 +485,17 @@ fn extract_from_pub_key_from_message(
Ok(pk)
}
+fn extract_bls_signing_bytes_from_message(
+ cbor_message: &CborBuffer,
+) -> Result<Vec<u8>, SignerError> {
+ let message = transaction_parse(cbor_message, true)?;
+
+ let unsigned_message_api = message.get_message();
+ let unsigned_message = UnsignedMessage::try_from(&unsigned_message_api)?;
+
+ Ok(unsigned_message.to_signing_bytes())
+}
+
pub fn verify_aggregated_signature(
signature: &SignatureBLS,
cbor_messages: &[CborBuffer],
@@ -505,9 +518,23 @@ pub fn verify_aggregated_signature(
};
// Hashes
- let hashes: Vec<_> = cbor_messages
+ let tmp: Result<Vec<_>, SignerError> = cbor_messages
+ .iter()
+ .map(|cbor_message| extract_bls_signing_bytes_from_message(cbor_message))
+ .collect();
+
+ let signing_bytes = match tmp {
+ Ok(bytes) => bytes,
+ Err(_) => {
+ return Err(SignerError::GenericString(
+ "An invalid message was provided".to_string(),
+ ));
+ }
+ };
+
+ let hashes = signing_bytes
.par_iter()
- .map(|cbor_message| bls_signatures::hash(cbor_message.as_ref()))
+ .map(|signing_bytes| bls_signatures::hash(signing_bytes.as_ref()))
.collect::<Vec<_>>();
Ok(bls_signatures::verify(&sig, &hashes, pks.as_slice()))

original_message: fix(bls): signature verification


message:
refactor(tests): new signature message
style(test): sign_bls_transactions use `dbg!` instead of `println!`

diff:
@@ -3,7 +3,8 @@ use std::convert::TryFrom;
use bip39::{Language, Seed};
use bls_signatures::Serialize;
use forest_address::Address;
-use forest_encoding::to_vec;
+use forest_encoding::{to_vec, Cbor};
+use forest_message::UnsignedMessage;
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
use rayon::prelude::*;
@@ -290,7 +291,7 @@ fn sign_bls_transaction() {
let bls_key =
PrivateKey::try_from(test_value["bls_private_key"].as_str().unwrap().to_string()).unwrap();
- println!("{}", bls_address.to_string());
+ dbg!(bls_address.to_string());
// Prepare message with BLS address
let message = UnsignedMessageAPI {
@@ -307,23 +308,25 @@ fn sign_bls_transaction() {
let raw_sig = transaction_sign_raw(&message, &bls_key).unwrap();
- println!("{}", hex::encode(raw_sig.as_bytes()));
+ dbg!(hex::encode(raw_sig.as_bytes()));
let sig = bls_signatures::Signature::from_bytes(&raw_sig.as_bytes()).expect("FIX ME");
let bls_pk = bls_signatures::PublicKey::from_bytes(&bls_pubkey).unwrap();
- let message_cbor = transaction_serialize(&message).expect("FIX ME");
+ let message = UnsignedMessage::try_from(&message).expect("FIX ME");
+ let message_cbor = message.marshal_cbor().expect("FIX ME");
- println!("{}", hex::encode(&message_cbor));
+ dbg!(hex::encode(&message_cbor));
- assert!(bls_pk.verify(sig, &message_cbor));
+ assert!(bls_pk.verify(sig, &message.to_signing_bytes()));
}
#[test]
fn test_verify_bls_signature() {
let test_value = common::load_test_vectors("../test_vectors/bls_signature.json").unwrap();
+ //TODO: update signature (what is the private key?)
let sig = Signature::try_from(test_value["sig"].as_str().unwrap().to_string()).unwrap();
let message =
CborBuffer(hex::decode(test_value["cbor"].as_str().unwrap().to_string()).unwrap());
@@ -437,7 +440,10 @@ fn payment_channel_creation_bls_signing() {
);
// First check transaction_serialize() in creating an unsigned message
- let result = transaction_serialize(&pch_create_message_api).unwrap();
+ let _ = transaction_serialize(&pch_create_message_api).unwrap();
+
+ let unsigned_message = UnsignedMessage::try_from(&pch_create_message_api).unwrap();
+ let bls_signing_bytes = unsigned_message.to_signing_bytes();
// Now check that we can generate a correct signature
let sig = transaction_sign_raw(&pch_create_message_api, &bls_key).unwrap();
@@ -446,7 +452,7 @@ fn payment_channel_creation_bls_signing() {
let bls_sig = bls_signatures::Serialize::from_bytes(&sig.as_bytes()).expect("FIX ME");
- assert!(bls_pkey.verify(bls_sig, &result));
+ assert!(bls_pkey.verify(bls_sig, &bls_signing_bytes));
}
#[test]


message: rename: get_signing_bytes to specify bls

diff:
@@ -997,15 +997,15 @@ pub fn get_cid(signed_message: JsValue) -> Result<String, JsValue> {
Ok(result)
}
-#[wasm_bindgen(js_name = getSigningBytes)]
-pub fn get_signing_bytes(unsigned_message: JsValue) -> Result<Vec<u8>, JsValue> {
+#[wasm_bindgen(js_name = getBlsSigningBytes)]
+pub fn get_bls_signing_bytes(unsigned_message: JsValue) -> Result<Vec<u8>, JsValue> {
set_panic_hook();
let unsigned_message_api: UnsignedMessageAPI = unsigned_message
.into_serde()
.map_err(|e| JsValue::from(format!("Error parsing parameters: {}", e)))?;
- let result = filecoin_signer::get_signing_bytes(unsigned_message_api)
+ let result = filecoin_signer::get_bls_signing_bytes(unsigned_message_api)
.map_err(|e| JsValue::from(format!("Error getting the cid: {}", e)))?;
Ok(result)

diff:
@@ -445,7 +445,7 @@ fn verify_bls_signature(
let sig = bls_signatures::Signature::from_bytes(signature.as_ref())?;
- let signing_bytes = get_signing_bytes(message)?;
+ let signing_bytes = get_bls_signing_bytes(message)?;
let result = pk.verify(sig, signing_bytes);
Ok(result)
@@ -1250,7 +1250,9 @@ pub fn get_cid(signed_message_api: SignedMessageAPI) -> Result<String, SignerErr
/// # Arguments
///
/// * `unsigned_message_api` - The unsigned message;
-pub fn get_signing_bytes(unsigned_message_api: UnsignedMessageAPI) -> Result<Vec<u8>, SignerError> {
+pub fn get_bls_signing_bytes(
+ unsigned_message_api: UnsignedMessageAPI,
+) -> Result<Vec<u8>, SignerError> {
let unsigned_message = UnsignedMessage::try_from(&unsigned_message_api)?;
let bytes = unsigned_message.to_signing_bytes();


message:
refactor(get_cid): use UnsignedMessageAPI
refactor(test): update `test_get_cid`
feat(get_cid): use MessageTxAPI

diff:
@@ -4,7 +4,7 @@ use std::convert::TryFrom;
use wasm_bindgen::prelude::*;
-use filecoin_signer::api::{MessageParams, SignedMessageAPI, UnsignedMessageAPI};
+use filecoin_signer::api::{MessageParams, MessageTxAPI, SignedMessageAPI, UnsignedMessageAPI};
use filecoin_signer::signature::Signature;
use filecoin_signer::{CborBuffer, PrivateKey};
@@ -984,14 +984,14 @@ pub fn verify_voucher_signature(
}
#[wasm_bindgen(js_name = getCid)]
-pub fn get_cid(signed_message: JsValue) -> Result<String, JsValue> {
+pub fn get_cid(message: JsValue) -> Result<String, JsValue> {
set_panic_hook();
- let signed_message_api: SignedMessageAPI = signed_message
+ let message_api: MessageTxAPI = message
.into_serde()
.map_err(|e| JsValue::from(format!("Error parsing parameters: {}", e)))?;
- let result = filecoin_signer::get_cid(signed_message_api)
+ let result = filecoin_signer::get_cid(message_api.get_message())
.map_err(|e| JsValue::from(format!("Error getting the cid: {}", e)))?;
Ok(result)

diff:
@@ -1231,16 +1231,17 @@ pub fn verify_voucher_signature(
}
}
-/// Return the CID of a signed message
+/// Return the CID of an unsigned message
///
/// # Arguments
///
-/// * `signed_message_api` - The signed message;
-pub fn get_cid(signed_message_api: SignedMessageAPI) -> Result<String, SignerError> {
- let signed_message = SignedMessage::try_from(&signed_message_api)?;
- let cbor_signed_message = to_vec(&signed_message)?;
+/// * `unsigned_message_api` - The unsigned message;
+pub fn get_cid(unsigned_message_api: UnsignedMessageAPI) -> Result<String, SignerError> {
+ let unsigned_message = UnsignedMessage::try_from(&unsigned_message_api)?;
- let cid = Cid::new_from_cbor(&cbor_signed_message, Blake2b256);
+ let raw_cid = unsigned_message.to_signing_bytes();
+ let cid = Cid::from_raw_cid(raw_cid)
+ .expect("FIX ME: to_signing_bytes is assumed to return ONLY the CID");
Ok(cid.to_string())
}

diff:
@@ -938,7 +938,7 @@ fn test_get_cid() {
serde_json::from_value(test_value["signed_message"].to_owned())
.expect("couldn't serialize signed message");
- let cid = get_cid(signed_message_api).unwrap();
+ let cid = get_cid(signed_message_api.message).unwrap();
assert_eq!(cid, expected_cid);
}

diff:
"data": "0wRrFJZFIVh8m0JD+f5C55YrxD6YAWtCXWYihrPTKdMfgMhYAy86MVhs43hSLXnV+47UReRIe8qFdHRJqFlreAE="
}
},
- "cid": "bafy2bzacebaiinljwwctblf7czp4zxwhz4747z6tpricgn5cumd4xhebftcvu"
+ "cid": "bafy2bzacedluvv7t2no5ujbuxtxi5mthioymz2wzorarxird736mz7wn3mucy"
}


message:
fix(generate): use CID for signature
rename: signer/utils to signer/examples

change_type: RENAME

diff:
@@ -65,9 +65,9 @@ fn run(num_messages: usize) {
.map(|(message_api, sk)| {
let message = UnsignedMessage::try_from(message_api).expect("FIXME");
- let message_cbor = to_vec(&message).expect("Cbor serialization failed");
+ let sign_bytes = message.to_signing_bytes();
- sk.sign(message_cbor)
+ sk.sign(sign_bytes)
})
.collect::<Vec<Signature>>();


message: refactor(get_cid): accept `MessageTxAPI`

diff:
@@ -991,7 +991,7 @@ pub fn get_cid(message: JsValue) -> Result<String, JsValue> {
.into_serde()
.map_err(|e| JsValue::from(format!("Error parsing parameters: {}", e)))?;
- let result = filecoin_signer::get_cid(message_api.get_message())
+ let result = filecoin_signer::get_cid(message_api)
.map_err(|e| JsValue::from(format!("Error getting the cid: {}", e)))?;
Ok(result)

diff:
@@ -1231,20 +1231,29 @@ pub fn verify_voucher_signature(
}
}
-/// Return the CID of an unsigned message
+/// Return the CID of a message
///
/// # Arguments
///
-/// * `unsigned_message_api` - The unsigned message;
-pub fn get_cid(unsigned_message_api: UnsignedMessageAPI) -> Result<String, SignerError> {
- let unsigned_message = UnsignedMessage::try_from(&unsigned_message_api)?;
+/// * `message_api` - The message;
+pub fn get_cid(message_api: MessageTxAPI) -> Result<String, SignerError> {
+ use forest_encoding::Cbor;
- let raw_cid = unsigned_message.to_signing_bytes();
- let cid = Cid::from_raw_cid(raw_cid)
- .expect("FIX ME: to_signing_bytes is assumed to return ONLY the CID");
+ match message_api {
+ MessageTxAPI::UnsignedMessageAPI(unsigned) => {
+ let unsigned_message = UnsignedMessage::try_from(&unsigned)?;
+ let cid = unsigned_message.cid()?;
Ok(cid.to_string())
}
+ MessageTxAPI::SignedMessageAPI(signed) => {
+ let signed_message = SignedMessage::try_from(&signed)?;
+ let cid = signed_message.cid()?;
+
+ Ok(cid.to_string())
+ }
+ }
+}
/// Return the bytes to sign of an unsigned message
///


message: remove: get_bls_signing_bytes

diff:
@@ -996,17 +996,3 @@ pub fn get_cid(message: JsValue) -> Result<String, JsValue> {
Ok(result)
}
-
-#[wasm_bindgen(js_name = getBlsSigningBytes)]
-pub fn get_bls_signing_bytes(unsigned_message: JsValue) -> Result<Vec<u8>, JsValue> {
- set_panic_hook();
-
- let unsigned_message_api: UnsignedMessageAPI = unsigned_message
- .into_serde()
- .map_err(|e| JsValue::from(format!("Error parsing parameters: {}", e)))?;
-
- let result = filecoin_signer::get_bls_signing_bytes(unsigned_message_api)
- .map_err(|e| JsValue::from(format!("Error getting the cid: {}", e)))?;
-
- Ok(result)
-}

diff:
@@ -445,7 +445,9 @@ fn verify_bls_signature(
let sig = bls_signatures::Signature::from_bytes(signature.as_ref())?;
- let signing_bytes = get_bls_signing_bytes(message)?;
+ let message = UnsignedMessage::try_from(&message)?;
+ let signing_bytes = message.to_signing_bytes();
+
let result = pk.verify(sig, signing_bytes);
Ok(result)
@@ -1254,18 +1256,3 @@ pub fn get_cid(message_api: MessageTxAPI) -> Result<String, SignerError> {
}
}
}
-
-/// Return the bytes to sign of an unsigned message
-///
-/// # Arguments
-///
-/// * `unsigned_message_api` - The unsigned message;
-pub fn get_bls_signing_bytes(
- unsigned_message_api: UnsignedMessageAPI,
-) -> Result<Vec<u8>, SignerError> {
- let unsigned_message = UnsignedMessage::try_from(&unsigned_message_api)?;
-
- let bytes = unsigned_message.to_signing_bytes();
-
- Ok(bytes)
-}

original_message: remove: get_bls_signing_bytes


message: refactor(test): new `get_cid`

diff:
@@ -326,7 +326,6 @@ fn sign_bls_transaction() {
fn test_verify_bls_signature() {
let test_value = common::load_test_vectors("../test_vectors/bls_signature.json").unwrap();
- //TODO: update signature (what is the private key?)
let sig = Signature::try_from(test_value["sig"].as_str().unwrap().to_string()).unwrap();
let message =
CborBuffer(hex::decode(test_value["cbor"].as_str().unwrap().to_string()).unwrap());
@@ -934,11 +933,10 @@ fn test_get_cid() {
let test_value = common::load_test_vectors("../test_vectors/get_cid.json").unwrap();
let expected_cid = test_value["cid"].as_str().unwrap().to_string();
- let signed_message_api: SignedMessageAPI =
- serde_json::from_value(test_value["signed_message"].to_owned())
+ let message_api: MessageTxAPI = serde_json::from_value(test_value["signed_message"].to_owned())
.expect("couldn't serialize signed message");
- let cid = get_cid(signed_message_api.message).unwrap();
+ let cid = get_cid(message_api).unwrap();
assert_eq!(cid, expected_cid);
}

original_message: refactor(test): new `get_cid`


message: refactor(js tests): remove redunant and fix sig

diff:
@@ -380,22 +380,16 @@ describe("verifySignature", function() {
if (process.env.PURE_JS) { itCall = it.skip }
itCall("verify BLS signature (1)", function() {
- const message = dataTxs[3].transaction
- const sig = dataTxs[3].sig
-
- const tx = filecoin_signer.transactionSerialize(message)
- const v = filecoin_signer.verifySignature(sig, tx)
-
- assert.strictEqual(v, true);
- })
-
- itCall("verify BLS signature (2)", function() {
const tc = dataTxs[4]
const signed_tx = filecoin_signer.transactionSign(tc.message, Buffer.from(tc.sk, "hex").toString("base64"));
console.log(signed_tx);
const raw_signature = filecoin_signer.transactionSignRaw(tc.message, Buffer.from(tc.sk, "hex").toString("base64"));
- console.log(Buffer.from(raw_signature).toString('hex'));
+
+ const hex_sig = Buffer.from(raw_signature).toString('hex');
+ console.log(hex_sig);
+ assert.strictEqual(hex_sig, tc.sig);
+
const signature = Buffer.from(signed_tx.signature.data, 'base64');
console.log(signature.toString('hex'));
const tx = filecoin_signer.transactionSerialize(tc.message)


message: deps(audit): update variable

diff:
@@ -16,7 +16,7 @@ serde_cbor = "0.11.1"
serde_bytes = "0.11.5"
rayon = "1"
-bls-signatures = "0.6.1"
+bls-signatures = "0.7.1"
# Crypto related
hex = { git = "https://github.com/Zondax/rust-hex", rev="6e35fb48999278c8c6c75b099baa4ea2a9d1d12b" }

original_message: deps(audit): update variable


message: deps(signer): downgrade `bls-signatures`

diff:
@@ -16,7 +16,7 @@ serde_cbor = "0.11.1"
serde_bytes = "0.11.5"
rayon = "1"
-bls-signatures = "0.7.1"
+bls-signatures = "0.6.1"
# Crypto related
hex = { git = "https://github.com/Zondax/rust-hex", rev="6e35fb48999278c8c6c75b099baa4ea2a9d1d12b" }


message: fix: icon bug

diff:
description = strip_html(page.content).substring(0, 120);
}
description = description ? description : pageTitle;
+
+ let iconType = 'image/png'
+ let faviconExt = theme.favicon.substr(theme.favicon.lastIndexOf('.') + 1, theme.favicon.length)
+ if (faviconExt === 'png') {
+ iconType = 'image/png'
+ } else if (faviconExt === 'svg') {
+ iconType = 'image/svg+xml'
+ } else if (faviconExt === 'gif') {
+ iconType = 'image/gif'
+ } else if (faviconExt === 'jpeg') {
+ iconType = 'image/jpeg'
+ } else if (faviconExt === 'jpg') {
+ iconType = 'image/jpeg'
+ } else if (faviconExt === 'ico') {
+ iconType = 'image/x-icon, image/vnd.microsoft.icon'
+ } else {
+ // more definitions
+ }
%>
<head>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<title><%= pageTitle %></title>
- <link rel="icon" type="image/png" href="<%- theme.favicon %>">
+ <link rel="icon" type="<%= iconType %>" href="<%- theme.favicon %>">
<link rel="stylesheet" type="text/css" href="/libs/awesome/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="/libs/materialize/css/materialize.min.css">

repo: blinkfox/hexo-theme-matery

original_message: fix: icon bug


message: update: rearrange four divs to fit small mobile, large mobile, pad and computer screen if wordcount is enabled.

diff:
-
+

<%- __('publishDate') %>:
<%- date(page.date, 'YYYY-MM-DD') %>
-
+

<% if (theme.wordCount.enable) { %>
<% if (theme.wordCount.postWordCount) { %>
-
+

<%- __('wordCount') %>:
<%= wordcount(page.content) %>
-
+

<% } %>
<% if (theme.wordCount.min2read) { %>
-
+

<%- __('readTimes') %>:
<%= min2read(page.content) %> <%= __('Minutes') %>
-
+

<% } %>
<% } %>
<% if (theme.busuanziStatistics && theme.busuanziStatistics.enable) { %>
-
+

<%- __('readCount') %>:

-
+

<% } %>

-

+

<%- page.content %>

diff:
@@ -1628,3 +1628,39 @@ footer .copy-right {
line-height: 48px;
font-size: 1.8rem;
}
+
+@media screen and (min-width: 368px) and (max-width: 767px) {
+ .info-break-policy {
+ word-break: keep-all;
+ float: left;
+ width: 50%;
+ }
+}
+
+@media screen and (min-width: 768px) {
+ .info-break-policy {
+ word-break: keep-all;
+ float: left;
+ margin-right: 15px;
+ }
+}
+
+@media screen and (max-width: 367px) {
+ .info-break-policy {
+ word-break: keep-all;
+ float: left;
+ width: 100%;
+ }
+
+ .custom-card {
+ padding: 0 2px !important;
+ }
+}
+
+.info-break-policy {
+ margin-bottom: 8px;
+}
+
+.clearfix {
+ clear: left;
+}


message: add: add shadow and caption to image in post

diff:
@@ -1628,3 +1628,22 @@ footer .copy-right {
line-height: 48px;
font-size: 1.8rem;
}
+
+.img_shadow {
+ box-shadow: 0px 0px 14px 1px #666
+}
+
+.img_margin {
+ margin: 25px auto 15px auto;
+}
+
+.center-caption {
+ color: #5f5f5f;
+ padding: 10px;
+ border-bottom: 1px solid #d9d9d9;
+}
+
+.caption {
+ text-align: center;
+ margin: 0 auto 15px auto;
+}


message: update: change the style of `some code`, because it's ugly on PC browser

diff:
@@ -1628,3 +1628,7 @@ footer .copy-right {
line-height: 48px;
font-size: 1.8rem;
}
+
+code {
+ font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier, monospace;
+}
\ No newline at end of file


message: fix: last fix introduced a new bug, which caused the toc unclickable

diff:
.toc-widget {
padding-left: 20px;
- z-index: -1;
}
.toc-widget .toc-title {

diff:
@@ -1552,6 +1552,7 @@ footer {
footer .social-statis {
margin-top: 10px;
+ position: relative;
}
footer a {


message: update: make summary absolute 3 lines to keep the cards neat

diff:
@@ -39,7 +39,7 @@ var featureImages = theme.featureImages;

-
<%- strip_html(page.prev.content).substring(0, 120) %>

+
<%- strip_html(page.prev.content).substring(0, 120) %>

<%= date(page.prev.date, config.date_format) %>
@@ -95,7 +95,7 @@ var featureImages = theme.featureImages;

-
<%- strip_html(page.content).substring(0, 120) %>

+
<%- strip_html(page.content).substring(0, 120) %>

<%= date(page.date, config.date_format) %>
@@ -153,7 +153,7 @@ var featureImages = theme.featureImages;

-
<%- strip_html(page.next.content).substring(0, 120) %>

+
<%- strip_html(page.next.content).substring(0, 120) %>

<%= date(page.next.date, config.date_format) %>
@@ -209,7 +209,7 @@ var featureImages = theme.featureImages;

-
<%- strip_html(page.content).substring(0, 120) %>

+
<%- strip_html(page.content).substring(0, 120) %>

<%= date(page.date, config.date_format) %>

diff:
-

+

<% if (post.summary && post.summary.length > 0) { %>
<%- post.summary %>
<% } else { %>

diff:
@@ -1747,3 +1747,47 @@ footer .copy-right {
.overflow-policy {
overflow: hidden;
}
+
+/* styles for '...' */
+.block-with-text {
+ /* hide text if it more than N lines */
+ overflow: hidden;
+ /* for set '...' in absolute position */
+ position: relative;
+ /* use this value to count block height */
+ line-height: 1.5em;
+ /* max-height = line-height (1.2) * lines max number (3) */
+ max-height: 4.5em;
+ /* fix problem when last visible word doesn't adjoin right side */
+ text-align: justify;
+ /* place for '...' */
+ margin-right: -1em;
+ padding-right: 1em;
+}
+
+/* create the ... style */
+.block-with-text:before {
+ /* points in the end */
+ content: '...';
+ /* absolute position */
+ position: absolute;
+ /* set position to right bottom corner of block */
+ right: 0;
+ bottom: 0;
+}
+
+/* hide ... if we have text, which is less than or equal to max lines */
+.block-with-text:after {
+ /* points in the end */
+ content: '';
+ /* absolute position */
+ position: absolute;
+ /* set position to right bottom corner of text */
+ right: 0;
+ /* set width and height */
+ width: 1em;
+ height: 1em;
+ margin-top: 0.2em;
+ /* bg color = bg color under block */
+ background: white;
+}


message: update: make summary longer to padding up the parent card

diff:
<% if (post.summary && post.summary.length > 0) { %>
<%- post.summary %>
<% } else { %>
- <%- strip_html(post.content).substring(0, 80) %>
+ <%- strip_html(post.content).substring(0, 120) %>
<% } %>
</div>
<div class="publish-info">

diff:
<% if (post.summary && post.summary.length > 0) { %>
<%- post.summary %>
<% } else { %>
- <%- strip_html(post.content).substring(0, 70) %>
+ <%- strip_html(post.content).substring(0, 120) %>
<% } %>
</div>
<div class="publish-info">


message: new feature: user-definable reprint policy

diff:
@@ -15,7 +15,7 @@ readTimes: Read Times
Minutes: Min
readMore: Read more
toc: TOC
-reprint: Reprint please specify
+reprint: Reprint policy
curr: Current
prev: Previous
next: Next
@@ -42,3 +42,29 @@ from: From
author: Author
link: Link
notag: No tag
+cc: Creative Commons License
+by: by
+under: is licensed under a
+licensed: " "
+cc_by_url: https://creativecommons.org/licenses/by/4.0/
+cc_by_name: Creative Commons Attribution 4.0 International License
+cc_by_nd_url: https://creativecommons.org/licenses/by-nd/4.0/
+cc_by_nd_name: Creative Commons Attribution-NoDerivatives 4.0 International License
+cc_by_sa_url: https://creativecommons.org/licenses/by-sa/4.0/
+cc_by_sa_name: Creative Commons Attribution-ShareAlike 4.0 International License
+cc_by_nc_url: https://creativecommons.org/licenses/by-nc/4.0/
+cc_by_nc_name: c
+cc_by_nc_nd_url: http://creativecommons.org/licenses/by-nc-nd/4.0/
+cc_by_nc_nd_name: Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License
+cc_by_nc_sa_url: http://creativecommons.org/licenses/by-nc-sa/4.0/
+cc_by_nc_sa_name: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License
+cc_0_url: https://creativecommons.org/publicdomain/zero/1.0/
+toTheExtentPossibleUnderLaw: To the extent possible under law,
+hasWaivedAllCopyrightAndRelatedOrNeighboringRightsTo: has waived all copyright and related or neighboring rights to
+workPublishedFrom: ". This work is published from:"
+cc_0_country: United States.
+cc_0_content: US
+noreprint: This article is not allowed to reprint.
+payreprint: Please contact the author to pay for reprinting.
+pleaseFollowReprintPolicy: Copied successfully, please follow the reprint policy of this article
+checkPolicy: more
\ No newline at end of file

diff:
<%- partial('_partial/share') %>
-

-

-
-   <%= __('reprint') %>:
-
- <a href="<%- config.url %>" class="b-link-green"><%- config.title %>
-
- <a href="<%- url_for(page.path) %>" class="b-link-green"><%= page.title %>
-

-

+ <%- partial('_partial/reprint-statement') %>

diff:
@@ -935,8 +935,8 @@ article .article-tags .chip {
}
#artDetail .reprint .reprint-tip {
- margin-right: 3px;
- font-size: 1.05rem;
+ margin: 0 3px 22px 0;
+ font-size: 1.5rem;
font-weight: 500;
}


message: bug fix: downgrade icon version

diff:
@@ -5,7 +5,7 @@ let reprintPolicy = page.reprintPolicy || theme.reprint.default || 'cc_by'
<% if (reprintPolicyEnabled) { %>

-
+
<%= __('reprint') %>

<% if (reprintPolicy === 'cc_by') { %>

original_message: bug fix: downgrade icon version


message: update: readme on reprint policy

diff:
@@ -345,11 +345,13 @@ Everything in the Front-matter option is **not required**. But I still recommend
| summary | null | Post summary, custom post summary content, if the attribute has a value, the post card summary will display the text, otherwise the program will automatically intercept part of the article as a summary |
| categories | null | Article classification, the classification of this topic represents a macroscopically large classification, only one article is recommended for one classification. |
| tags | null | Post label, a post can have multiple labels |
+| reprintPolicy | cc_by | Post reprint policy, value could be one of cc_by, cc_by_nd, cc_by_sa, cc_by_nc, cc_by_nc_nd, cc_by_nc_sa, cc0, noreprint and pay |
> **Note**:
> 1. post's featured piature will take remainder if not writing the `img` property,and chose the featured picture of theme to let all of post's picture **have their own characteristics**.
> 2. The value of `date` should try to ensure that each article is unique, because `Gitalk` and `Gitment` recognize `id` in this topic are uniquely identified by the value of `date`.
> 3. If you want to set the ability to read the verification password for the article, you should not only set the value of the password with SHA256 encryption in Front-matter, but also activate the configuration in the theme `_config.yml`.
+> 4. you can define reprint policy for a single article in the front-matter of the specific md file using this key: reprintPolicy
The following are examples of the post's `Front-matter`.

original_message: update: readme on reprint policy


message: hidpp20: fix last feature discovery

diff:
@@ -341,7 +341,10 @@ hidpp20_feature_set_get(struct hidpp20_device *device)
flist = zalloc(feature_count * sizeof(struct hidpp20_feature));
- for (i = 0; i < feature_count; i++) {
+ /* feature set count does not include the root feature as documented here:
+ * https://6xq.net/git/lars/lshidpp.git/plain/doc/logitech_hidpp_2.0_specification_draft_2012-06-04.pdf
+ **/
+ for (i = 0; i <= feature_count; i++) {
rc = hidpp20_feature_set_get_feature_id(device,
feature_index,
i,

license: MIT License

repo: libratbag/libratbag

original_message:
hidpp20: fix last feature discovery

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>


message:
support sector size that are not multiple of 16
Squashed the comment into this

diff:
@@ -1132,6 +1132,12 @@ hidpp20_onboard_profiles_read_sector(struct hidpp20_device *device,
count = sector_size;
for (offset = 0; offset < sector_size; offset += 16) {
+ /*
+ * the firmware replies with an ERR_INVALID_ARGUMENT error
+ * if we try to read past sector_size - 16, so when we are left with
+ * less than 16 bytes to read we need to read from sector_size - 16
+ */
+ offset = (sector_size - offset < 16) ? sector_size - 16 : offset;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[2], offset);
buf = msg;
rc = hidpp20_request_command(device, &buf);

original_message:
support sector size that are not multiple of 16

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>

Squashed the comment into this
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>


diff:
@@ -334,17 +334,17 @@ hidpp20_feature_set_get(struct hidpp20_device *device)
if (rc < 0)
return rc;
- feature_count = (uint8_t)rc;
+ /* feature set count does not include the root feature as documented here:
+ * https://6xq.net/git/lars/lshidpp.git/plain/doc/logitech_hidpp_2.0_specification_draft_2012-06-04.pdf
+ **/
+ feature_count = ((uint8_t)rc) + 1;
- if (!feature_count)
+ if (feature_count == 1)
return -ENOTSUP;
flist = zalloc((feature_count + 1) * sizeof(struct hidpp20_feature));
- /* feature set count does not include the root feature as documented here:
- * https://6xq.net/git/lars/lshidpp.git/plain/doc/logitech_hidpp_2.0_specification_draft_2012-06-04.pdf
- **/
- for (i = 0; i <= feature_count; i++) {
+ for (i = 0; i < feature_count; i++) {
rc = hidpp20_feature_set_get_feature_id(device,
feature_index,
i,

original_message:
hidpp20: store correct size for feature list

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>


diff:
@@ -495,6 +495,11 @@ hidpp20drv_read_resolution_dpi_2201(struct ratbag_device *device)
drv_data->sensors[0].dpi_max);
drv_data->num_sensors = rc;
+
+ /* if 0x8100 has already been enumerated we already have the supported
+ * number of resolutions and shouldn't overwrite it
+ */
+ if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100))
drv_data->num_resolutions = drv_data->num_sensors;
return 0;

original_message:
hidpp20: fix resolution count detection
fix #343

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>


message:
hidpp20: fix special button mapping
fix

diff:
@@ -2200,16 +2200,15 @@ static const enum ratbag_button_action_special hidpp20_profiles_specials[] = {
[0x02] = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT,
[0x03] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP,
[0x04] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN,
- [0x05] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT,
- [0x06] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP,
+ [0x05] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP,
+ [0x06] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT,
[0x07] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE,
- [0x08] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
- [0x09] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
+ [0x08] = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP,
+ [0x09] = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN,
[0x0a] = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP,
[0x0b] = RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE,
- [0x0c] = RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL,
- [0x0d ... 0xff] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
+ [0x0c ... 0xff] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
};
enum ratbag_button_action_special

original_message:
hidpp20: fix special button mapping
fix #348

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>


diff:
#define HIDPP_CAP_KBD_REPROGRAMMABLE_KEYS_1b00 (1 << 4)
#define HIDPP_CAP_COLOR_LED_EFFECTS_8070 (1 << 5)
#define HIDPP_CAP_ONBOARD_PROFILES_8100 (1 << 6)
+#define HIDPP_CAP_LED_SW_CONTROL (1 << 7)
struct hidpp20drv_data {
struct hidpp20_device *dev;
@@ -63,7 +64,7 @@ struct hidpp20drv_data {
unsigned num_controls;
struct hidpp20_control_id *controls;
struct hidpp20_profiles *profiles;
- struct hidpp20_color_led_zone_info *led_infos;
+ union hidpp20_generic_led_zone_info led_infos;
unsigned int num_profiles;
unsigned int num_resolutions;
@@ -257,7 +258,7 @@ hidpp20drv_read_led(struct ratbag_led *led)
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return;
- led_info = &drv_data->led_infos[led->index];
+ led_info = &drv_data->led_infos.color_leds[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
@@ -677,11 +678,11 @@ hidpp20drv_read_color_leds(struct ratbag_device *device)
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return 0;
- free(drv_data->led_infos);
- drv_data->led_infos = NULL;
+ free(drv_data->led_infos.color_leds);
+ drv_data->led_infos.color_leds = NULL;
drv_data->num_leds = 0;
- rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos);
+ rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos.color_leds);
if (rc > 0) {
drv_data->num_leds = rc;
@@ -999,7 +1000,7 @@ hidpp20drv_remove(struct ratbag_device *device)
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(drv_data->profiles);
- free(drv_data->led_infos);
+ free(drv_data->led_infos.color_leds);
free(drv_data->controls);
free(drv_data->sensors);
if (drv_data->dev)

diff:
@@ -70,7 +70,32 @@ hidpp20_feature_get_name(uint16_t feature)
break;
}
+ return str;
+}
+
+const char*
+hidpp20_sw_led_control_get_mode_string(const enum hidpp20_led_sw_ctrl_led_mode mode)
+{
+ static char numeric[8];
+ const char* str;
+
+ switch (mode)
+ {
+ CASE_RETURN_STRING(HIDPP20_LED_MODE_OFF);
+ CASE_RETURN_STRING(HIDPP20_LED_MODE_ON);
+ CASE_RETURN_STRING(HIDPP20_LED_MODE_BLINK);
+ CASE_RETURN_STRING(HIDPP20_LED_MODE_RAMP_UP);
+ CASE_RETURN_STRING(HIDPP20_LED_MODE_RAMP_DOWN);
+ CASE_RETURN_STRING(HIDPP20_LED_MODE_BREATHING);
+ CASE_RETURN_STRING(HIDPP20_LED_MODE_HEARTBEAT);
+ CASE_RETURN_STRING(HIDPP20_LED_MODE_TRAVEL);
+ default:
+ sprintf_safe(numeric, "%#4x", mode);
+ str = numeric;
+ break;
+ }
#undef CASE_RETURN_STRING
+
return str;
}
@@ -400,6 +425,277 @@ hidpp20_batterylevel_get_battery_level(struct hidpp20_device *device,
return msg.msg.parameters[2];
}
+/* -------------------------------------------------------------------------- */
+/* 0x1300: Non-RGB led support */
+/* -------------------------------------------------------------------------- */
+
+#define CMD_LED_SW_CONTROL_GET_LED_COUNT 0x00
+#define CMD_LED_SW_CONTROL_GET_LED_INFO 0x10
+#define CMD_LED_SW_CONTROL_GET_SW_CTRL 0x20
+#define CMD_LED_SW_CONTROL_SET_SW_CTRL 0x30
+#define CMD_LED_SW_CONTROL_GET_LED_STATE 0x40
+#define CMD_LED_SW_CONTROL_SET_LED_STATE 0x50
+#define CMD_LED_SW_CONTROL_GET_NV_CONFIG 0x60
+
+static bool hidpp20_led_sw_control_check_state(uint16_t state)
+{
+ switch (state)
+ {
+ case HIDPP20_LED_MODE_ON:
+ case HIDPP20_LED_MODE_OFF:
+ case HIDPP20_LED_MODE_BLINK:
+ case HIDPP20_LED_MODE_TRAVEL:
+ case HIDPP20_LED_MODE_RAMP_UP:
+ case HIDPP20_LED_MODE_RAMP_DOWN:
+ case HIDPP20_LED_MODE_HEARTBEAT:
+ case HIDPP20_LED_MODE_BREATHING:
+ return true;
+ }
+
+ return false;
+}
+
+int hidpp20_led_sw_control_read_leds(struct hidpp20_device* device,
+ struct hidpp20_led_sw_ctrl_led_info** info_list)
+{
+ int rc;
+ struct hidpp20_led_sw_ctrl_led_info *i_list, *info;
+ unsigned i;
+ uint8_t num_infos;
+
+ rc = hidpp20_led_sw_control_get_led_count(device);
+
+ if (rc < 0)
+ return rc;
+
+ num_infos = rc;
+
+ if(rc == 0) {
+ *info_list = NULL;
+ return 0;
+ }
+
+ i_list = zalloc(rc * sizeof(struct hidpp20_led_sw_ctrl_led_info));
+
+ for (i = 0; i < num_infos; i++) {
+ info = &i_list[i];
+ info->index = i;
+
+ rc = hidpp20_led_sw_control_get_led_info(device, i, info);
+
+ if (rc != 0)
+ goto err;
+
+ hidpp_log_raw(&device->base, "non-color led %d: type: %d supports: %d\n",
+ info->index,
+ info->type,
+ info->caps);
+ }
+
+ *info_list = i_list;
+ return num_infos;
+
+err:
+ free(i_list);
+ return rc;
+}
+
+int hidpp20_led_sw_control_get_led_count(struct hidpp20_device* device)
+{
+ union hidpp20_message msg = {
+ .msg = {
+ .report_id = REPORT_ID_LONG,
+ .address = CMD_LED_SW_CONTROL_GET_LED_COUNT,
+ .device_idx = device->index,
+ },
+ };
+
+ uint8_t feature_idx;
+
+ feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
+ if (feature_idx == 0) {
+ return -ENOTSUP;
+ }
+
+ msg.msg.sub_id = feature_idx;
+
+ if (hidpp20_request_command(device, &msg)) {
+ return -ENOTSUP;
+ }
+
+ return msg.msg.parameters[0];
+}
+
+int hidpp20_led_sw_control_get_led_info(struct hidpp20_device* device,
+ uint8_t led_idx,
+ struct hidpp20_led_sw_ctrl_led_info *info)
+{
+ union hidpp20_message msg = {
+ .msg = {
+ .report_id = REPORT_ID_LONG,
+ .address = CMD_LED_SW_CONTROL_GET_LED_INFO,
+ .device_idx = device->index,
+ },
+ };
+ struct hidpp20_led_sw_ctrl_led_info *params;
+ uint8_t feature_idx;
+
+ feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
+ if (feature_idx == 0)
+ return -ENOTSUP;
+
+ msg.msg.sub_id = feature_idx;
+ msg.msg.parameters[0] = led_idx;
+
+ if (hidpp20_request_command(device, &msg))
+ // Only error possible is an invalid index, which means the led doesn't exist
+ return -ENOENT;
+
+ params = (struct hidpp20_led_sw_ctrl_led_info*) msg.msg.parameters;
+ params->type = hidpp_be_u16_to_cpu(params->type);
+ params->caps = hidpp_be_u16_to_cpu(params->caps);
+
+ *info = *params;
+
+ return 0;
+}
+
+bool hidpp20_led_sw_control_get_sw_ctrl(struct hidpp20_device* device)
+{
+ uint8_t feature_idx;
+ int rc;
+ union hidpp20_message msg = {
+ .msg = {
+ .report_id = REPORT_ID_SHORT,
+ .address = CMD_LED_SW_CONTROL_GET_SW_CTRL,
+ .device_idx = device->index,
+ },
+ };
+
+ feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
+
+ if (feature_idx == 0)
+ return -ENOTSUP;
+
+ msg.msg.sub_id = feature_idx;
+
+ rc = hidpp20_request_command(device, &msg);
+
+ if (rc) {
+ return -ENOTSUP;
+ }
+
+ return msg.msg.parameters[0];
+}
+
+int hidpp20_led_sw_control_set_sw_ctrl(struct hidpp20_device* device, bool ctrl)
+{
+ uint8_t feature_idx;
+ union hidpp20_message msg = {
+ .msg = {
+ .report_id = REPORT_ID_SHORT,
+ .address = CMD_LED_SW_CONTROL_SET_SW_CTRL,
+ .device_idx = device->index,
+ },
+ };
+
+ feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
+
+ if (feature_idx == 0)
+ return -ENOTSUP;
+
+ msg.msg.sub_id = feature_idx;
+ msg.msg.parameters[0] = ctrl;
+
+ if (hidpp20_request_command(device, &msg))
+ return -EINVAL;
+
+ return 0;
+}
+
+int hidpp20_led_sw_control_get_led_state(struct hidpp20_device* device,
+ uint8_t led_idx,
+ struct hidpp20_led_sw_ctrl_led_state *out)
+{
+ int rc;
+ uint8_t feature_idx;
+ union hidpp20_message msg = {
+ .msg = {
+ .report_id = REPORT_ID_LONG,
+ .address = CMD_LED_SW_CONTROL_GET_LED_STATE,
+ .device_idx = device->index,
+ }
+ };
+ struct hidpp20_led_sw_ctrl_led_state *state;
+
+ feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
+
+ if (feature_idx == 0) {
+ return -ENOTSUP;
+ }
+
+ msg.msg.sub_id = feature_idx;
+
+ msg.msg.parameters[0] = led_idx;
+
+ rc = hidpp20_request_command(device, &msg);
+
+ if (rc)
+ return -ENOENT;
+
+ state = (struct hidpp20_led_sw_ctrl_led_state*) msg.msg.parameters;
+
+ // This field has to be stored in little-endian
+ state->mode = state->mode;
+
+ if (state->mode == HIDPP20_LED_MODE_BREATHING) {
+ // Only parameters that is reported by these LEDs is brightness when breathing
+ state->breathing.brightness = hidpp_be_u16_to_cpu(state->breathing.brightness);
+ }
+
+ *out = *state;
+
+ return 0;
+}
+
+int hidpp20_led_sw_control_set_led_state(struct hidpp20_device* device,
+ const struct hidpp20_led_sw_ctrl_led_state *state)
+{
+ int rc;
+ uint8_t feature_idx;
+ union hidpp20_message msg = {
+ .msg = {
+ .report_id = REPORT_ID_LONG,
+ .address = CMD_LED_SW_CONTROL_SET_LED_STATE,
+ .device_idx = device->index,
+ }
+ };
+
+ feature_idx = hidpp_root_get_feature_idx(device, HIDPP_PAGE_LED_SW_CONTROL);
+
+ if (feature_idx == 0)
+ return -ENOTSUP;
+
+ msg.msg.sub_id = feature_idx;
+
+ if (!hidpp20_led_sw_control_check_state(state->mode))
+ return -EINVAL;
+
+ msg.msg.parameters[0] = state->index;
+ hidpp_set_unaligned_be_u16(&msg.msg.parameters[1], state->mode);
+ hidpp_set_unaligned_be_u16(&msg.msg.parameters[3], state->blink.index);
+ hidpp_set_unaligned_be_u16(&msg.msg.parameters[5], state->blink.on_time);
+ hidpp_set_unaligned_be_u16(&msg.msg.parameters[7], state->blink.off_time);
+
+ rc = hidpp20_request_command(device, &msg);
+
+ if (rc)
+ return -EINVAL;
+
+ return 0;
+}
+
+
/* -------------------------------------------------------------------------- */
/* 0x1b00: KBD reprogrammable keys and mouse buttons */
/* -------------------------------------------------------------------------- */

diff:
@@ -126,6 +126,130 @@ int hidpp20_batterylevel_get_battery_level(struct hidpp20_device *device,
uint16_t *level,
uint16_t *next_level);
+/* -------------------------------------------------------------------------- */
+/* 0x1300: LED software control */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_LED_SW_CONTROL 0x1300
+
+enum hidpp20_led_sw_ctrl_led_type {
+ HIDPP20_LED_TYPE_BATTERY = 0x01,
+ HIDPP20_LED_TYPE_DPI,
+ HIDPP20_LED_TYPE_PROFILE,
+ HIDPP20_LED_TYPE_LOGO,
+ HIDPP20_LED_TYPE_COSMETIC
+};
+
+enum hidpp20_led_sw_ctrl_led_mode {
+ HIDPP20_LED_MODE_OFF = 0x01,
+ HIDPP20_LED_MODE_ON = 0x02,
+ HIDPP20_LED_MODE_BLINK = 0x04,
+ HIDPP20_LED_MODE_TRAVEL = 0x08,
+ HIDPP20_LED_MODE_RAMP_UP = 0x10,
+ HIDPP20_LED_MODE_RAMP_DOWN = 0x20,
+ HIDPP20_LED_MODE_HEARTBEAT = 0x40,
+ HIDPP20_LED_MODE_BREATHING = 0x80
+};
+
+#define HIDPP20_LED_SW_CONTROL_LED_INDEX_ALL 0xff
+
+struct hidpp20_led_sw_ctrl_led_info {
+ uint8_t index;
+ uint8_t type;
+ uint8_t physical_count;
+ uint16_t caps;
+ uint8_t nvconfig_caps;
+} __attribute__((packed));
+_Static_assert(sizeof(struct hidpp20_led_sw_ctrl_led_info) == 6, "Invalid size");
+
+struct hidpp20_led_sw_ctrl_led_state {
+ uint8_t index;
+ uint16_t mode;
+ union {
+ struct {
+ uint16_t brightness;
+ uint16_t period;
+ uint16_t timeout;
+ } breathing;
+ struct {
+ uint16_t unused;
+ uint16_t delay;
+ } traveling;
+ struct {
+ uint16_t index;
+ uint16_t on_time;
+ uint16_t off_time;
+ } blink;
+ struct {
+ /* Logical information to display on the LED
+ * Meaning and value range depend on the LED
+ */
+ uint16_t index;
+ } on;
+ };
+} __attribute__((packed));
+_Static_assert(sizeof(struct hidpp20_led_sw_ctrl_led_state) == 9, "Invalid size");
+
+int hidpp20_led_sw_control_read_leds(struct hidpp20_device* device,
+ struct hidpp20_led_sw_ctrl_led_info** info_list);
+
+const char*
+hidpp20_sw_led_control_get_mode_string(const enum hidpp20_led_sw_ctrl_led_mode mode);
+
+int hidpp20_led_sw_control_read_leds(struct hidpp20_device* device,
+ struct hidpp20_led_sw_ctrl_led_info** info_list);
+
+/**
+ * Retrieves the number of non-RGB logical leds from the mouse
+ * @return the led count or a negative errno
+ */
+int hidpp20_led_sw_control_get_led_count(struct hidpp20_device* device);
+
+/**
+ * Retrieves the info for a given logical led
+ * @return 0 or a negative errno in case of error
+ */
+int hidpp20_led_sw_control_get_led_info(struct hidpp20_device* device,
+ uint8_t led_idx,
+ struct hidpp20_led_sw_ctrl_led_info* info);
+
+/**
+ * Reads all LEDs on the device for their current state
+ * @return 0 on success or a negative errno
+ */
+int hidpp20_led_sw_control_read_leds(struct hidpp20_device* device,
+ struct hidpp20_led_sw_ctrl_led_info** info_list);
+
+/**
+ * Get the current sw control value
+ * @return 0 if the leds are sofware-controlled 1 if they are firmware controlled
+ */
+bool hidpp20_led_sw_control_get_sw_ctrl(struct hidpp20_device* device);
+
+/**
+ * Sets who controls the LEDs
+ * ctrl = 0, the software controls the LEDs
+ * ctrl = 1, the firmware controls the LEDs
+ * @return 0 on success or a negative errno
+ */
+int hidpp20_led_sw_control_set_sw_ctrl(struct hidpp20_device* device, bool ctrl);
+
+/**
+ * Gets the state of a LED
+ * @return 0 on success or a negative errno
+ */
+int hidpp20_led_sw_control_get_led_state(struct hidpp20_device* device,
+ uint8_t led_idx,
+ struct hidpp20_led_sw_ctrl_led_state* state);
+
+/**
+ * Sets the state of a LED
+ * The LED index should be passed in the state structure
+ * @return 0 on success or a negative errno
+ */
+int hidpp20_led_sw_control_set_led_state(struct hidpp20_device* device,
+ const struct hidpp20_led_sw_ctrl_led_state* state);
+
/* -------------------------------------------------------------------------- */
/* 0x1b00: KBD reprogrammable keys and mouse buttons */
/* -------------------------------------------------------------------------- */
@@ -258,7 +382,6 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
#define HIDPP_PAGE_COLOR_LED_EFFECTS 0x8070
-
struct hidpp20_color_led_info;
struct hidpp20_color_led_zone_info;
@@ -362,6 +485,14 @@ struct hidpp20_color_led_info {
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_info) == 5, "Invalid size");
+struct hidpp20_non_color_led_zone_info {
+ uint8_t index;
+ uint8_t type;
+ uint16_t capabilities;
+ uint8_t nv_config;
+} __attribute__((packed));
+_Static_assert(sizeof(struct hidpp20_non_color_led_zone_info) == 5, "Invalid size");
+
struct hidpp20_color_led_zone_info {
uint8_t index;
uint16_t location;
@@ -370,6 +501,11 @@ struct hidpp20_color_led_zone_info {
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_info) == 5, "Invalid size");
+union hidpp20_generic_led_zone_info {
+ struct hidpp20_led_sw_ctrl_led_info* leds;
+ struct hidpp20_color_led_zone_info* color_leds;
+};
+
struct hidpp20_color {
uint8_t red;
uint8_t green;
@@ -610,5 +746,3 @@ hidpp20_onboard_profiles_allocate_sector(struct hidpp20_profiles *profiles)
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_MOUSE_BUTTON_SPY 0x8110
-
-

original_message:
hidpp20: add support for non-RGB LEDs, feature 0x1300

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>


diff:
#define HIDPP_CAP_KBD_REPROGRAMMABLE_KEYS_1b00 (1 << 4)
#define HIDPP_CAP_COLOR_LED_EFFECTS_8070 (1 << 5)
#define HIDPP_CAP_ONBOARD_PROFILES_8100 (1 << 6)
-#define HIDPP_CAP_LED_SW_CONTROL (1 << 7)
+#define HIDPP_CAP_LED_SW_CONTROL_1300 (1 << 7)
struct hidpp20drv_data {
struct hidpp20_device *dev;
@@ -247,17 +247,63 @@ hidpp20drv_read_button(struct ratbag_button *button)
}
static void
-hidpp20drv_read_led(struct ratbag_led *led)
+hidpp20drv_read_led_1300(struct ratbag_led *led, struct hidpp20drv_data* data)
{
- struct ratbag_device *device = led->profile->device;
- struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
- struct hidpp20_profile *profile;
- struct hidpp20_color_led_zone_info *led_info;
- struct hidpp20_led *h_led;
+ struct hidpp20_led_sw_ctrl_led_state state;
+ struct hidpp20_led_sw_ctrl_led_info *info;
+ int rc;
- if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
+ info = &data->led_infos.leds[led->index];
+
+ rc = hidpp20_led_sw_control_get_led_state(data->dev, led->index, &state);
+
+ if (rc != 0)
return;
+ led->colordepth = RATBAG_LED_COLORDEPTH_MONOCHROME;
+
+ switch (info->type)
+ {
+ case HIDPP20_LED_TYPE_LOGO:
+ led->type = RATBAG_LED_TYPE_LOGO;
+ break;
+ case HIDPP20_LED_TYPE_BATTERY:
+ led->type = RATBAG_LED_TYPE_BATTERY;
+ break;
+ case HIDPP20_LED_TYPE_DPI:
+ led->type = RATBAG_LED_TYPE_DPI;
+ break;
+ case HIDPP20_LED_TYPE_PROFILE:
+ case HIDPP20_LED_TYPE_COSMETIC:
+ led->type = RATBAG_LED_TYPE_SIDE;
+ break;
+ }
+
+ switch (state.mode)
+ {
+ case HIDPP20_LED_MODE_OFF:
+ led->mode = RATBAG_LED_OFF;
+ break;
+ case HIDPP20_LED_MODE_ON:
+ led->mode = RATBAG_LED_ON;
+ break;
+ case HIDPP20_LED_MODE_BREATHING:
+ led->mode = RATBAG_LED_BREATHING;
+ led->hz = state.breathing.period;
+ led->brightness = state.breathing.brightness;
+ break;
+ default:
+ led->mode = RATBAG_LED_ON;
+ }
+}
+
+static void
+hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_data)
+{
+ struct hidpp20_profile *profile;
+ struct hidpp20_led *h_led;
+ struct hidpp20_color_led_zone_info* led_info;
+
led_info = &drv_data->led_infos.color_leds[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
@@ -285,6 +331,18 @@ hidpp20drv_read_led(struct ratbag_led *led)
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
}
+static void
+hidpp20drv_read_led(struct ratbag_led *led)
+{
+ struct ratbag_device *device = led->profile->device;
+ struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
+
+ if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
+ hidpp20drv_read_led_8070(led, drv_data);
+ else if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
+ hidpp20drv_read_led_1300(led, drv_data);
+}
+
static int
hidpp20drv_write_button_1b04(struct ratbag_button *button,
const struct ratbag_button_action *action)
@@ -389,16 +447,60 @@ hidpp20drv_update_button(struct ratbag_button *button,
}
static int
-hidpp20drv_update_led(struct ratbag_led *led)
+hidpp20drv_update_led_1300(struct ratbag_led *led, struct hidpp20drv_data *data)
{
- struct ratbag_profile *profile = led->profile;
- struct ratbag_device *device = profile->device;
- struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
- struct hidpp20_profile *h_profile;
- struct hidpp20_led *h_led;
+ const uint16_t led_caps = data->led_infos.leds[led->index].caps;
+ struct hidpp20_led_sw_ctrl_led_state h_led;
+ int rc;
- if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
+ h_led.index = led->index;
+
+ switch(led->mode)
+ {
+ case RATBAG_LED_BREATHING:
+ h_led.mode = HIDPP20_LED_MODE_BREATHING;
+ h_led.breathing.brightness = led->brightness;
+ h_led.breathing.period = led->hz;
+ h_led.breathing.timeout = 300;
+ break;
+ case RATBAG_LED_OFF:
+ h_led.mode = HIDPP20_LED_MODE_OFF;
+ h_led.on.index = HIDPP20_LED_SW_CONTROL_LED_INDEX_ALL;
+ break;
+ case RATBAG_LED_ON:
+ h_led.mode = HIDPP20_LED_MODE_ON;
+ h_led.on.index = HIDPP20_LED_SW_CONTROL_LED_INDEX_ALL;
+ break;
+ case RATBAG_LED_CYCLE:
+ return -ENOTSUP;
+ }
+
+ if (!(h_led.mode & led_caps)) {
+ hidpp_log_error(&data->dev->base, "LED %d does not support effect %s(%04x), supports %04x\n",
+ led->index, hidpp20_sw_led_control_get_mode_string(h_led.mode),
+ h_led.mode, led_caps);
return -ENOTSUP;
+ }
+
+ if (!hidpp20_led_sw_control_get_sw_ctrl(data->dev)) {
+ rc = hidpp20_led_sw_control_set_sw_ctrl(data->dev, true);
+ if (rc)
+ return rc;
+ }
+
+ rc = hidpp20_led_sw_control_set_led_state(data->dev, &h_led);
+ if (rc)
+ return rc;
+
+ return RATBAG_SUCCESS;
+}
+
+static int
+hidpp20drv_update_led_8070(struct ratbag_led *led, struct ratbag_profile* profile,
+ struct hidpp20drv_data *drv_data)
+{
+ struct hidpp20_profile *h_profile;
+ struct hidpp20_led *h_led;
h_profile = &drv_data->profiles->profiles[profile->index];
@@ -430,6 +532,22 @@ hidpp20drv_update_led(struct ratbag_led *led)
return RATBAG_SUCCESS;
}
+static int
+hidpp20drv_update_led(struct ratbag_led *led)
+{
+ struct ratbag_profile *profile = led->profile;
+ struct ratbag_device *device = profile->device;
+ struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
+
+ if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
+ return hidpp20drv_update_led_8070(led, profile, drv_data);
+
+ if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
+ return hidpp20drv_update_led_1300(led, drv_data);
+
+ return RATBAG_ERROR_CAPABILITY;
+}
+
static int
hidpp20drv_current_profile(struct ratbag_device *device)
{
@@ -692,6 +810,27 @@ hidpp20drv_read_color_leds(struct ratbag_device *device)
return rc;
}
+static int
+hidpp20drv_read_leds(struct ratbag_device *device)
+{
+ struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
+ int rc;
+
+ if (!(drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300))
+ return 0;
+
+ free(drv_data->led_infos.leds);
+ drv_data->led_infos.leds = NULL;
+
+ rc = hidpp20_led_sw_control_read_leds(drv_data->dev, &drv_data->led_infos.leds);
+ if (rc > 0) {
+ drv_data->num_leds = rc;
+ rc = 0;
+ }
+
+ return rc;
+}
+
static void
hidpp20drv_read_profile_8100(struct ratbag_profile *profile, unsigned int index)
{
@@ -847,6 +986,20 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
device->num_leds = drv_data->num_leds;
break;
}
+ case HIDPP_PAGE_LED_SW_CONTROL: {
+ log_debug(ratbag, "device has non-rgb leds\n");
+ drv_data->capabilities |= HIDPP_CAP_LED_SW_CONTROL_1300;
+
+ if (drv_data->capabilities & HIDPP_PAGE_COLOR_LED_EFFECTS)
+ return 0;
+
+ if (hidpp20drv_read_leds(device))
+ return 0;
+
+ ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_LED);
+ device->num_leds = drv_data->num_leds;
+ break;
+ }
case HIDPP_PAGE_ONBOARD_PROFILES: {
log_debug(ratbag, "device has onboard profiles\n");
drv_data->capabilities |= HIDPP_CAP_ONBOARD_PROFILES_8100;

diff:
@@ -1460,7 +1460,9 @@ enum ratbag_led_mode {
enum ratbag_led_type {
RATBAG_LED_TYPE_UNKNOWN = -1,
RATBAG_LED_TYPE_LOGO = 0,
- RATBAG_LED_TYPE_SIDE
+ RATBAG_LED_TYPE_SIDE,
+ RATBAG_LED_TYPE_BATTERY,
+ RATBAG_LED_TYPE_DPI,
};
/**

original_message:
driver-hidpp20: add feature discovery for 0x1300
fix #325

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>


message: tools: use new RATBAG_LED_TYPEs

diff:
@@ -63,6 +63,12 @@ led_type_to_str(enum ratbag_led_type type)
case RATBAG_LED_TYPE_SIDE:
str = "side";
break;
+ case RATBAG_LED_TYPE_BATTERY:
+ str = "battery";
+ break;
+ case RATBAG_LED_TYPE_DPI:
+ str = "dpi";
+ break;
}
return str;

original_message:
tools: use new RATBAG_LED_TYPEs

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>


change_type: ADD

diff:
+[Device]
+Name=Logitech G703
+DeviceMatch=usb:046d:c087
+Driver=hidpp20
+Svg=logitech-g703.svg
+

diff:
+logitech-g403.svg
\ No newline at end of file

diff:
@@ -278,6 +278,7 @@ data_files = files(
'data/devices/logitech-g700s.device',
'data/devices/logitech-g700s-wireless.device',
'data/devices/logitech-g700-wireless.device',
+ 'data/devices/logitech-g703.device',
'data/devices/logitech-g7.device',
'data/devices/logitech-g900.device',
'data/devices/logitech-g900-wireless.device',
@@ -325,6 +326,7 @@ gnome_svg_files = files(
'data/gnome/logitech-g500s.svg',
'data/gnome/logitech-g502.svg',
'data/gnome/logitech-g700.svg',
+ 'data/gnome/logitech-g703.svg',
'data/gnome/logitech-g9.svg',
'data/gnome/logitech-g900.svg',
'data/gnome/roccat-kone-xtd.svg',

original_message:
data: add device file and svg for Logitech G703 wired

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>


diff:
@@ -2545,6 +2545,7 @@ hidpp20_onboard_profiles_commit(struct hidpp20_device *device,
{
struct hidpp20_profile *profile;
unsigned int i;
+ bool enabled_profile = false;
int rc;
for (i = 0; i < profiles_list->num_profiles; i++) {
@@ -2556,6 +2557,19 @@ hidpp20_onboard_profiles_commit(struct hidpp20_device *device,
i);
if (rc < 0)
return rc;
+
+ enabled_profile = true;
+ }
+ }
+
+ if (!enabled_profile) {
+ if (profiles_list->num_profiles > 0) {
+ profiles_list->profiles[0].enabled = 1;
+ rc = hidpp20_onboard_profiles_write_profile(device,
+ profiles_list,
+ 0);
+ if (rc < 0)
+ return rc;
}
}

original_message:
hidpp20: force at least one profile to be enabled
fix #428

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>


diff:
@@ -196,7 +196,7 @@ static int ratbagd_profile_is_active(sd_bus *bus,
sd_bus_error *error)
{
struct ratbagd_profile *profile = userdata;
- bool is_active;
+ int is_active;
is_active = !!ratbag_profile_is_active(profile->lib_profile);
@@ -315,7 +315,7 @@ ratbagd_profile_set_enabled(sd_bus *bus,
sd_bus_error *error)
{
struct ratbagd_profile *profile = userdata;
- bool enabled;
+ int enabled;
int r;
r = sd_bus_message_read(m, "b", &enabled);
@@ -345,7 +345,7 @@ ratbagd_profile_is_enabled(sd_bus *bus,
sd_bus_error *error)
{
struct ratbagd_profile *profile = userdata;
- bool enabled = ratbag_profile_is_enabled(profile->lib_profile) != 0;
+ int enabled = ratbag_profile_is_enabled(profile->lib_profile) != 0;
return sd_bus_message_append(reply, "b", enabled);
}

diff:
@@ -165,7 +165,7 @@ ratbagd_resolution_is_active(sd_bus *bus,
{
struct ratbagd_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
- bool is_active;
+ int is_active;
is_active = !!ratbag_resolution_is_active(lib_resolution);
@@ -183,7 +183,7 @@ ratbagd_resolution_is_default(sd_bus *bus,
{
struct ratbagd_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
- bool is_default;
+ int is_default;
is_default = !!ratbag_resolution_is_default(lib_resolution);

original_message:
ratbagd: ensure boolean passed to sd_* are 4 bytes wide

fix #433

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>


message: Use the configured application namespace

diff:
@@ -78,7 +78,7 @@ abstract class AbstractAnnotator {
$this->_io = $io;
$this->setConfig($config);
- $namespace = $this->getConfig(static::CONFIG_PLUGIN) ?: 'App';
+ $namespace = $this->getConfig(static::CONFIG_PLUGIN) ?: Configure::read('App.namespace', 'App');
$namespace = str_replace('/', '\\', $namespace);
$this->setConfig(static::CONFIG_NAMESPACE, $namespace);
}

repo: dereuromark/cakephp-ide-helper


message: Add uses statement

diff:
@@ -4,6 +4,7 @@ namespace IdeHelper\Annotator;
use Bake\View\Helper\DocBlockHelper;
use Cake\Console\Shell;
use Cake\Core\App;
+use Cake\Core\Configure;
use Cake\Core\InstanceConfigTrait;
use Cake\View\View;
use IdeHelper\Annotation\AbstractAnnotation;

original_message: Add uses statement


message: Allow TableFinderTask to work with abstract models

diff:
@@ -7,7 +7,7 @@ use Cake\ORM\Association;
use Cake\ORM\Query;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
-use Exception;
+use Throwable;
use ReflectionClass;
class TableFinderTask extends ModelTask {
@@ -75,7 +75,7 @@ class TableFinderTask extends ModelTask {
$finderMap = $this->invokeProperty($behaviors, '_finderMap');
$customFinders = array_merge($customFinders, array_keys($finderMap));
- } catch (Exception $exception) {
+ } catch (Throwable $exception) {
}
$allFinders = array_merge($allFinders, $customFinders);


message: Make PHP 5.6 compatible

diff:
@@ -7,6 +7,7 @@ use Cake\ORM\Association;
use Cake\ORM\Query;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
+use Exception;
use ReflectionClass;
use Throwable;
@@ -75,6 +76,7 @@ class TableFinderTask extends ModelTask {
$finderMap = $this->invokeProperty($behaviors, '_finderMap');
$customFinders = array_merge($customFinders, array_keys($finderMap));
+ } catch (Exception $exception) {
} catch (Throwable $exception) {
}

original_message: Make PHP 5.6 compatible


message: Add test to show that plugin through btm associations use the wrong property name

diff:
@@ -24,6 +24,7 @@ class ModelAnnotatorTest extends TestCase {
public $fixtures = [
'plugin.ide_helper.foo',
'plugin.ide_helper.wheels',
+ 'plugin.ide_helper.bar_bars',
];
/**
@@ -106,7 +107,7 @@ class ModelAnnotatorTest extends TestCase {
public function testAnnotate() {
$annotator = $this->_getAnnotatorMock([]);
- $expectedContent = str_replace("\r\n", "\n", file_get_contents(TEST_FILES . 'Model/Table/FooTable.php'));
+ $expectedContent = str_replace("\r\n", "\n", file_get_contents(TEST_FILES . 'Model/Table/BarBarsTable.php'));
$callback = function($value) use ($expectedContent) {
$value = str_replace(["\r\n", "\r"], "\n", $value);
if ($value !== $expectedContent) {
@@ -116,12 +117,12 @@ class ModelAnnotatorTest extends TestCase {
};
$annotator->expects($this->once())->method('_storeFile')->with($this->anything(), $this->callback($callback));
- $path = APP . 'Model/Table/FooTable.php';
+ $path = APP . 'Model/Table/BarBarsTable.php';
$annotator->annotate($path);
$output = (string)$this->out->output();
- $this->assertTextContains(' -> 11 annotations added', $output);
+ $this->assertTextContains(' -> 14 annotations added', $output);
}
/**

diff:
+<?php
+namespace App\Model\Entity;
+
+use Cake\ORM\Entity;
+
+class BarBar extends Entity {
+}

diff:
@@ -4,4 +4,17 @@ namespace App\Model\Table;
use Cake\ORM\Table;
class BarBarsTable extends Table {
+
+ public function initialize(array $config) {
+ parent::initialize($config);
+
+ $this->belongsTo('Foo');
+ $this->belongsToMany('Houses', [
+ 'className' => 'Awesome.Houses',
+ 'through' => 'Awesome.Windows',
+ ]);
+ $this->addBehavior('Tools.Confirmable');
+ $this->addBehavior('Timestamp');
+ $this->addBehavior('MyNamespace/MyPlugin.My');
+ }
}

diff:
@@ -6,7 +6,7 @@ namespace App\Controller;
* @property \App\Model\Table\BarBarsTable $BarBars
* @property \Shim\Controller\Component\SessionComponent $Session
*
- * @method \Cake\ORM\Entity[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
+ * @method \App\Model\Entity\BarBar[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class BarController extends AppController {

diff:
+<?php
+namespace App\Model\Table;
+
+use Cake\ORM\Table;
+
+/**
+ * @property \App\Model\Table\FooTable|\Cake\ORM\Association\BelongsTo $Foo
+ * @property \Awesome\Model\Table\HousesTable|\Cake\ORM\Association\BelongsToMany $Houses
+ * @property \Awesome\Model\Table\WindowsTable|\Cake\ORM\Association\HasMany $Windows
+ *
+ * @method \App\Model\Entity\BarBar get($primaryKey, $options = [])
+ * @method \App\Model\Entity\BarBar newEntity($data = null, array $options = [])
+ * @method \App\Model\Entity\BarBar[] newEntities(array $data, array $options = [])
+ * @method \App\Model\Entity\BarBar|bool save(\Cake\Datasource\EntityInterface $entity, $options = [])
+ * @method \App\Model\Entity\BarBar|bool saveOrFail(\Cake\Datasource\EntityInterface $entity, $options = [])
+ * @method \App\Model\Entity\BarBar patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
+ * @method \App\Model\Entity\BarBar[] patchEntities($entities, array $data, array $options = [])
+ * @method \App\Model\Entity\BarBar findOrCreate($search, callable $callback = null, $options = [])
+ *
+ * @mixin \Tools\Model\Behavior\ConfirmableBehavior
+ * @mixin \Cake\ORM\Behavior\TimestampBehavior
+ * @mixin \MyNamespace\MyPlugin\Model\Behavior\MyBehavior
+ */
+class BarBarsTable extends Table {
+
+ public function initialize(array $config) {
+ parent::initialize($config);
+
+ $this->belongsTo('Foo');
+ $this->belongsToMany('Houses', [
+ 'className' => 'Awesome.Houses',
+ 'through' => 'Awesome.Windows',
+ ]);
+ $this->addBehavior('Tools.Confirmable');
+ $this->addBehavior('Timestamp');
+ $this->addBehavior('MyNamespace/MyPlugin.My');
+ }
+}

diff:
@@ -26,6 +26,10 @@ class FooTable extends Table {
public function initialize(array $config) {
parent::initialize($config);
+ $this->belongsTo('BarBars');
+ $this->belongsTo('Houses', [
+ 'className' => 'Awesome.Houses'
+ ]);
$this->addBehavior('Tools.Confirmable');
$this->addBehavior('Timestamp');
$this->addBehavior('MyNamespace/MyPlugin.My');


message: Fix issue with plugin btm with through associations

diff:
@@ -200,7 +200,7 @@ class ModelAnnotator extends AbstractAnnotator {
$association = $tableAssociations->get($key);
$type = get_class($association);
- $name = $association->getAlias();
+ list(, $name) = pluginSplit($association->getAlias());
$table = $association->getClassName() ?: $association->getAlias();
$className = App::className($table, 'Model/Table', 'Table') ?: static::CLASS_TABLE;
@@ -217,13 +217,13 @@ class ModelAnnotator extends AbstractAnnotator {
}
$className = App::className($through, 'Model/Table', 'Table') ?: static::CLASS_TABLE;
-
+ list(, $throughName) = pluginSplit($through);
$type = HasMany::class;
- if (isset($associations[$type][$through])) {
+ if (isset($associations[$type][$throughName])) {
continue;
}
- $associations[$type][$through] = $className;
+ $associations[$type][$throughName] = $className;
}
return $associations;


message: Add test to show that virtual association properties break

diff:
@@ -24,4 +24,13 @@ class Wheel extends Entity {
return 'Virtual Two';
}
+ /**
+ * @param \App\Model\Entity\Wheel[] $wheels
+ *
+ * @return \App\Model\Entity\Wheel[]
+ */
+ protected function _getWheels($wheels = []) {
+ return $wheels;
+ }
+
}

diff:
@@ -34,4 +34,13 @@ class Wheel extends Entity {
return 'Virtual Two';
}
+ /**
+ * @param \App\Model\Entity\Wheel[] $wheels
+ *
+ * @return \App\Model\Entity\Wheel[]
+ */
+ protected function _getWheels($wheels = []) {
+ return $wheels;
+ }
+
}


message: Fix issue with virtual association property

diff:
@@ -57,15 +57,11 @@ class EntityAnnotator extends AbstractAnnotator {
$propertyHintMap = $helper->buildEntityPropertyHintTypeMap($schema);
$propertyHintMap = $this->buildExtendedEntityPropertyHintTypeMap($schema, $helper) + $propertyHintMap;
$propertyHintMap += $this->buildVirtualPropertyHintTypeMap($content);
+ $propertyHintMap += $helper->buildEntityAssociationHintTypeMap($schema);
$propertyHintMap = array_filter($propertyHintMap);
$annotations = $helper->propertyHints($propertyHintMap);
- $associationHintMap = $helper->buildEntityAssociationHintTypeMap($schema);
-
- if ($associationHintMap) {
- $annotations = array_merge($annotations, $helper->propertyHints($associationHintMap));
- }
foreach ($annotations as $key => $annotation) {
$annotationObject = AnnotationFactory::createFromString($annotation);


message: Fix controller model annotation

diff:
@@ -271,13 +271,11 @@ class ControllerAnnotator extends AbstractAnnotator {
return null;
}
- if ($this->getConfig(static::CONFIG_PLUGIN)) {
- $pluginModelClass = $this->getConfig(static::CONFIG_PLUGIN) . '.' . $modelClass;
- $className = App::className($pluginModelClass, 'Model/Table', 'Table');
- } else {
- $className = App::className($modelClass, 'Model/Table', 'Table');
+ if ($this->getConfig(static::CONFIG_PLUGIN) && strpos($modelClass, '.') === false) {
+ $modelClass = $this->getConfig(static::CONFIG_PLUGIN) . '.' . $modelClass;
}
+ $className = App::className($modelClass, 'Model/Table', 'Table');
if (!$className) {
return null;
}

diff:
+<?php
+
+/**
+ * Class HousesController
+ */
+class HousesController
+{
+
+}

original_message: Fix controller model annotation


message: Add test for plugin controller annotations

diff:
@@ -127,4 +127,51 @@ class ControllerAnnotatorTest extends TestCase {
return $this->getMockBuilder(ControllerAnnotator::class)->setMethods(['_storeFile'])->setConstructorArgs([$this->io, $params])->getMock();
}
+ /**
+ * @return void
+ */
+ public function testAnnotateWithPluginController()
+ {
+ $annotator = $this->_getAnnotatorMock([]);
+
+ $expectedContent = str_replace("\r\n", "\n", file_get_contents(TEST_FILES . 'Controller/HousesController.php'));
+ $callback = function ($value) use ($expectedContent) {
+ $value = str_replace(["\r\n", "\r"], "\n", $value);
+ if ($value !== $expectedContent) {
+ $this->_displayDiff($expectedContent, $value);
+ }
+
+ return $value === $expectedContent;
+ };
+ $annotator->expects($this->once())
+ ->method('_storeFile')
+ ->with($this->anything(), $this->callback($callback));
+
+ $path = TEST_ROOT . '/plugins/Controllers/src/Controller/HousesController.php';
+ $annotator->setConfig(ControllerAnnotator::CONFIG_PLUGIN, 'Controllers');
+ $annotator->annotate($path);
+
+ $output = (string)$this->out->output();
+
+ $this->assertTextContains(' -> 1 annotation added.', $output);
+ }
+
+ /**
+ * @return void
+ */
+ public function testAnnotateWithPluginControllerNoModel()
+ {
+ $annotator = $this->_getAnnotatorMock([]);
+
+ $annotator->expects($this->never())
+ ->method('_storeFile');
+
+ $path = TEST_ROOT . '/plugins/Controllers/src/Controller/GenericController.php';
+ $annotator->setConfig(ControllerAnnotator::CONFIG_PLUGIN, 'Awesome');
+ $annotator->annotate($path);
+
+ $output = (string)$this->out->output();
+
+ $this->assertTextNotContains(' -> 1 annotation added.', $output);
+ }
}

diff:
@@ -40,7 +40,8 @@ class ModelTaskTest extends TestCase {
'WheelsExtra' => '\App\Model\Table\WheelsExtraTable::class',
'Wheels' => '\App\Model\Table\WheelsTable::class',
'Awesome.Houses' => '\Awesome\Model\Table\HousesTable::class',
- 'Awesome.Windows' => '\Awesome\Model\Table\WindowsTable::class'
+ 'Awesome.Windows' => '\Awesome\Model\Table\WindowsTable::class',
+ 'Controllers.Houses' => '\Controllers\Model\Table\HousesTable::class',
];
$map = array_shift($result);
$this->assertSame($expectedMap, $map);

diff:
@@ -41,6 +41,7 @@ class TableAssociationTaskTest extends TestCase {
'Wheels' => '\Cake\ORM\Association\BelongsTo::class',
'Awesome.Houses' => '\Cake\ORM\Association\BelongsTo::class',
'Awesome.Windows' => '\Cake\ORM\Association\BelongsTo::class',
+ 'Controllers.Houses' => '\Cake\ORM\Association\BelongsTo::class',
];
$map = array_shift($result);
$this->assertSame($expectedMap, $map);

diff:
@@ -76,6 +76,7 @@ Type::build('timestamp')
Plugin::load('IdeHelper', ['path' => ROOT . DS, 'autoload' => true]);
Plugin::load('Shim', ['path' => ROOT . DS . 'vendor/dereuromark/cakephp-shim/', 'autoload' => true]);
Plugin::load('Awesome', ['path' => TEST_ROOT . 'plugins/Awesome/', 'autoload' => true]);
+Plugin::load('Controllers', ['path' => TEST_ROOT . 'plugins/Controllers/', 'autoload' => true]);
Plugin::load('MyBehavior/MyPlugin', ['path' => TEST_ROOT . 'plugins/MyBehavior/MyPlugin/', 'autoload' => true]);
DispatcherFactory::add('Routing');

change_type: DELETE

diff:
-<?php
-
-/**
- * Class HousesController
- */
-class HousesController
-{
-
-}

diff:
+<?php
+namespace Controllers\Controller;
+
+use \Cake\Controller\Controller;
+
+class GenericController extends Controller
+{
+ public $modelClass = false;
+}

diff:
+<?php
+namespace Controllers\Controller;
+
+use \Cake\Controller\Controller;
+
+class HousesController extends Controller
+{
+}

diff:
+<?php
+namespace Controllers\Model\Table;
+
+use Cake\ORM\Table;
+
+class HousesTable extends Table {
+}

diff:
+<?php
+namespace Controllers\Controller;
+
+use \Cake\Controller\Controller;
+
+/**
+ * @property \Controllers\Model\Table\HousesTable $Houses
+ */
+class HousesController extends Controller
+{
+}

diff:
@@ -65,6 +65,7 @@ namespace PHPSTORM_META {
'Wheels' => \App\Model\Table\WheelsTable::class,
'Awesome.Houses' => \Awesome\Model\Table\HousesTable::class,
'Awesome.Windows' => \Awesome\Model\Table\WindowsTable::class,
+ 'Controllers.Houses' => \Controllers\Model\Table\HousesTable::class,
])
);
@@ -100,6 +101,7 @@ namespace PHPSTORM_META {
'Wheels' => \App\Model\Table\WheelsTable::class,
'Awesome.Houses' => \Awesome\Model\Table\HousesTable::class,
'Awesome.Windows' => \Awesome\Model\Table\WindowsTable::class,
+ 'Controllers.Houses' => \Controllers\Model\Table\HousesTable::class,
])
);
@@ -128,6 +130,7 @@ namespace PHPSTORM_META {
'Wheels' => \Cake\ORM\Association\BelongsToMany::class,
'Awesome.Houses' => \Cake\ORM\Association\BelongsToMany::class,
'Awesome.Windows' => \Cake\ORM\Association\BelongsToMany::class,
+ 'Controllers.Houses' => \Cake\ORM\Association\BelongsToMany::class,
])
);
@@ -145,6 +148,7 @@ namespace PHPSTORM_META {
'Wheels' => \Cake\ORM\Association\BelongsTo::class,
'Awesome.Houses' => \Cake\ORM\Association\BelongsTo::class,
'Awesome.Windows' => \Cake\ORM\Association\BelongsTo::class,
+ 'Controllers.Houses' => \Cake\ORM\Association\BelongsTo::class,
])
);
@@ -171,6 +175,7 @@ namespace PHPSTORM_META {
'Wheels' => \Cake\ORM\Association\HasMany::class,
'Awesome.Houses' => \Cake\ORM\Association\HasMany::class,
'Awesome.Windows' => \Cake\ORM\Association\HasMany::class,
+ 'Controllers.Houses' => \Cake\ORM\Association\HasMany::class,
])
);
@@ -188,6 +193,7 @@ namespace PHPSTORM_META {
'Wheels' => \Cake\ORM\Association\HasOne::class,
'Awesome.Houses' => \Cake\ORM\Association\HasOne::class,
'Awesome.Windows' => \Cake\ORM\Association\HasOne::class,
+ 'Controllers.Houses' => \Cake\ORM\Association\HasOne::class,
])
);
@@ -205,6 +211,7 @@ namespace PHPSTORM_META {
'Wheels' => \App\Model\Table\WheelsTable::class,
'Awesome.Houses' => \Awesome\Model\Table\HousesTable::class,
'Awesome.Windows' => \Awesome\Model\Table\WindowsTable::class,
+ 'Controllers.Houses' => \Controllers\Model\Table\HousesTable::class,
])
);


message: Create a test to show that abstract parent classes break the model annotator

diff:
@@ -244,4 +244,31 @@ class ModelAnnotatorTest extends TestCase {
return $this->getMockBuilder(ModelAnnotator::class)->setMethods(['_storeFile'])->setConstructorArgs([$this->io, $params])->getMock();
}
+ /**
+ * @return void
+ */
+ public function testAnnotateProtectedParent()
+ {
+ $annotator = $this->_getAnnotatorMock([]);
+
+ $expectedContent = str_replace("\r\n", "\n", file_get_contents(TEST_FILES . 'Model/Table/BarBarsAbstractTable.php'));
+ $callback = function ($value) use ($expectedContent) {
+ $value = str_replace(["\r\n", "\r"], "\n", $value);
+ if ($value !== $expectedContent) {
+ $this->_displayDiff($expectedContent, $value);
+ }
+
+ return $value === $expectedContent;
+ };
+ $annotator->expects($this->once())
+ ->method('_storeFile')
+ ->with($this->anything(), $this->callback($callback));
+
+ $path = APP . 'Model/Table/BarBarsAbstractTable.php';
+ $annotator->annotate($path);
+
+ $output = (string)$this->out->output();
+
+ $this->assertTextContains(' -> 14 annotations added', $output);
+ }
}

diff:
@@ -30,6 +30,8 @@ class ModelTaskTest extends TestCase {
$this->assertCount(3, $result);
$expectedMap = [
+ 'Abstract' => '\App\Model\Table\AbstractTable::class',
+ 'BarBarsAbstract' => '\App\Model\Table\BarBarsAbstractTable::class',
'BarBars' => '\App\Model\Table\BarBarsTable::class',
'Callbacks' => '\App\Model\Table\CallbacksTable::class',
'Cars' => '\App\Model\Table\CarsTable::class',

diff:
@@ -30,6 +30,8 @@ class TableAssociationTaskTest extends TestCase {
$this->assertCount(4, $result);
$expectedMap = [
+ 'Abstract' => '\Cake\ORM\Association\BelongsTo::class',
+ 'BarBarsAbstract' => '\Cake\ORM\Association\BelongsTo::class',
'BarBars' => '\Cake\ORM\Association\BelongsTo::class',
'Callbacks' => '\Cake\ORM\Association\BelongsTo::class',
'Cars' => '\Cake\ORM\Association\BelongsTo::class',

diff:
+<?php
+namespace App\Model\Entity;
+
+use Cake\ORM\Entity;
+
+class BarBarsAbstract extends Entity {
+}

diff:
+<?php
+
+namespace App\Model\Table;
+
+use Cake\ORM\Table;
+
+/**
+ * Class AbstractTable
+ */
+abstract class AbstractTable extends Table
+{
+ /**
+ * @param array $config
+ * @return void
+ */
+ public function initialize(array $config)
+ {
+ parent::initialize($config);
+
+ $this->addBehavior('Timestamp');
+ }
+}

diff:
+<?php
+namespace App\Model\Table;
+
+use Cake\ORM\Table;
+
+class BarBarsAbstractTable extends AbstractTable {
+
+ /**
+ * @param array $config
+ * @return void
+ */
+ public function initialize(array $config) {
+ parent::initialize($config);
+
+ $this->setTable('bar_bars');
+ $this->belongsTo('Foo');
+ $this->belongsToMany('Houses', [
+ 'className' => 'Awesome.Houses',
+ 'through' => 'Awesome.Windows',
+ ]);
+ $this->addBehavior('Tools.Confirmable');
+ $this->addBehavior('MyNamespace/MyPlugin.My');
+ }
+
+}

diff:
+<?php
+namespace App\Model\Table;
+
+use Cake\ORM\Table;
+
+/**
+ * @property \App\Model\Table\FooTable&\Cake\ORM\Association\BelongsTo $Foo
+ * @property \Awesome\Model\Table\HousesTable&\Cake\ORM\Association\BelongsToMany $Houses
+ * @property \Awesome\Model\Table\WindowsTable&\Cake\ORM\Association\HasMany $Windows
+ *
+ * @method \App\Model\Entity\BarBarsAbstract get($primaryKey, $options = [])
+ * @method \App\Model\Entity\BarBarsAbstract newEntity($data = null, array $options = [])
+ * @method \App\Model\Entity\BarBarsAbstract[] newEntities(array $data, array $options = [])
+ * @method \App\Model\Entity\BarBarsAbstract|false save(\Cake\Datasource\EntityInterface $entity, $options = [])
+ * @method \App\Model\Entity\BarBarsAbstract saveOrFail(\Cake\Datasource\EntityInterface $entity, $options = [])
+ * @method \App\Model\Entity\BarBarsAbstract patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
+ * @method \App\Model\Entity\BarBarsAbstract[] patchEntities($entities, array $data, array $options = [])
+ * @method \App\Model\Entity\BarBarsAbstract findOrCreate($search, callable $callback = null, $options = [])
+ *
+ * @mixin \Cake\ORM\Behavior\TimestampBehavior
+ * @mixin \Tools\Model\Behavior\ConfirmableBehavior
+ * @mixin \MyNamespace\MyPlugin\Model\Behavior\MyBehavior
+ */
+class BarBarsAbstractTable extends AbstractTable {
+
+ /**
+ * @param array $config
+ * @return void
+ */
+ public function initialize(array $config) {
+ parent::initialize($config);
+
+ $this->setTable('bar_bars');
+ $this->belongsTo('Foo');
+ $this->belongsToMany('Houses', [
+ 'className' => 'Awesome.Houses',
+ 'through' => 'Awesome.Windows',
+ ]);
+ $this->addBehavior('Tools.Confirmable');
+ $this->addBehavior('MyNamespace/MyPlugin.My');
+ }
+
+}

diff:
@@ -54,6 +54,8 @@ namespace PHPSTORM_META {
override(
\Cake\Datasource\ModelAwareTrait::loadModel(0),
map([
+ 'Abstract' => \App\Model\Table\AbstractTable::class,
+ 'BarBarsAbstract' => \App\Model\Table\BarBarsAbstractTable::class,
'BarBars' => \App\Model\Table\BarBarsTable::class,
'Callbacks' => \App\Model\Table\CallbacksTable::class,
'Cars' => \App\Model\Table\CarsTable::class,
@@ -90,6 +92,8 @@ namespace PHPSTORM_META {
override(
\Cake\ORM\Locator\LocatorInterface::get(0),
map([
+ 'Abstract' => \App\Model\Table\AbstractTable::class,
+ 'BarBarsAbstract' => \App\Model\Table\BarBarsAbstractTable::class,
'BarBars' => \App\Model\Table\BarBarsTable::class,
'Callbacks' => \App\Model\Table\CallbacksTable::class,
'Cars' => \App\Model\Table\CarsTable::class,
@@ -119,6 +123,8 @@ namespace PHPSTORM_META {
override(
\Cake\ORM\Table::belongToMany(0),
map([
+ 'Abstract' => \Cake\ORM\Association\BelongsToMany::class,
+ 'BarBarsAbstract' => \Cake\ORM\Association\BelongsToMany::class,
'BarBars' => \Cake\ORM\Association\BelongsToMany::class,
'Callbacks' => \Cake\ORM\Association\BelongsToMany::class,
'Cars' => \Cake\ORM\Association\BelongsToMany::class,
@@ -137,6 +143,8 @@ namespace PHPSTORM_META {
override(
\Cake\ORM\Table::belongsTo(0),
map([
+ 'Abstract' => \Cake\ORM\Association\BelongsTo::class,
+ 'BarBarsAbstract' => \Cake\ORM\Association\BelongsTo::class,
'BarBars' => \Cake\ORM\Association\BelongsTo::class,
'Callbacks' => \Cake\ORM\Association\BelongsTo::class,
'Cars' => \Cake\ORM\Association\BelongsTo::class,
@@ -164,6 +172,8 @@ namespace PHPSTORM_META {
override(
\Cake\ORM\Table::hasMany(0),
map([
+ 'Abstract' => \Cake\ORM\Association\HasMany::class,
+ 'BarBarsAbstract' => \Cake\ORM\Association\HasMany::class,
'BarBars' => \Cake\ORM\Association\HasMany::class,
'Callbacks' => \Cake\ORM\Association\HasMany::class,
'Cars' => \Cake\ORM\Association\HasMany::class,
@@ -182,6 +192,8 @@ namespace PHPSTORM_META {
override(
\Cake\ORM\Table::hasOne(0),
map([
+ 'Abstract' => \Cake\ORM\Association\HasOne::class,
+ 'BarBarsAbstract' => \Cake\ORM\Association\HasOne::class,
'BarBars' => \Cake\ORM\Association\HasOne::class,
'Callbacks' => \Cake\ORM\Association\HasOne::class,
'Cars' => \Cake\ORM\Association\HasOne::class,
@@ -200,6 +212,8 @@ namespace PHPSTORM_META {
override(
\Cake\ORM\TableRegistry::get(0),
map([
+ 'Abstract' => \App\Model\Table\AbstractTable::class,
+ 'BarBarsAbstract' => \App\Model\Table\BarBarsAbstractTable::class,
'BarBars' => \App\Model\Table\BarBarsTable::class,
'Callbacks' => \App\Model\Table\CallbacksTable::class,
'Cars' => \App\Model\Table\CarsTable::class,


message: Fix the issue where things break when the parent class is abstract

diff:
@@ -295,15 +295,20 @@ class ModelAnnotator extends AbstractAnnotator {
$behaviors = $this->_extractBehaviors($map);
$parentClass = get_parent_class($table);
+ $parentBehaviors = [];
if (isset($this->_cache[$parentClass])) {
$parentBehaviors = $this->_cache[$parentClass];
} else {
+ try {
/** @var \Cake\ORM\Table $parent */
$parent = new $parentClass();
$object = $parent->behaviors();
$map = $this->_invokeProperty($object, '_loaded');
$this->_cache[$parentClass] = $parentBehaviors = $this->_extractBehaviors($map);
+ } catch (Exception $e) {
+ } catch (Throwable $e) {
+ }
}
$result = array_diff_key($behaviors, $parentBehaviors);


message: Use reflection to determine if parent class can be instantiated for php 5.6 compatibility

diff:
@@ -299,16 +299,17 @@ class ModelAnnotator extends AbstractAnnotator {
if (isset($this->_cache[$parentClass])) {
$parentBehaviors = $this->_cache[$parentClass];
} else {
- try {
+ $parentReflection = new \ReflectionClass($parentClass);
+ if (!$parentReflection->isInstantiable()) {
+ return $behaviors;
+ }
+
/** @var \Cake\ORM\Table $parent */
$parent = new $parentClass();
$object = $parent->behaviors();
$map = $this->_invokeProperty($object, '_loaded');
$this->_cache[$parentClass] = $parentBehaviors = $this->_extractBehaviors($map);
- } catch (Exception $e) {
- } catch (Throwable $e) {
- }
}
$result = array_diff_key($behaviors, $parentBehaviors);